Commit 313da6d3 authored by Dennis Tang's avatar Dennis Tang

Merge remote-tracking branch 'origin/master' into 43446-new-cluster-page-tabs

parents a5217676 9c296194
...@@ -342,7 +342,7 @@ group :development, :test do ...@@ -342,7 +342,7 @@ group :development, :test do
gem 'capybara', '~> 2.15' gem 'capybara', '~> 2.15'
gem 'capybara-screenshot', '~> 1.0.0' gem 'capybara-screenshot', '~> 1.0.0'
gem 'selenium-webdriver', '~> 3.5' gem 'selenium-webdriver', '~> 3.12'
gem 'spring', '~> 2.0.0' gem 'spring', '~> 2.0.0'
gem 'spring-commands-rspec', '~> 1.0.4' gem 'spring-commands-rspec', '~> 1.0.4'
...@@ -384,7 +384,7 @@ group :test do ...@@ -384,7 +384,7 @@ group :test do
gem 'test-prof', '~> 0.2.5' gem 'test-prof', '~> 0.2.5'
end end
gem 'octokit', '~> 4.8' gem 'octokit', '~> 4.9'
gem 'mail_room', '~> 0.9.1' gem 'mail_room', '~> 0.9.1'
......
...@@ -115,7 +115,7 @@ GEM ...@@ -115,7 +115,7 @@ GEM
mime-types (>= 1.16) mime-types (>= 1.16)
cause (0.1) cause (0.1)
charlock_holmes (0.7.6) charlock_holmes (0.7.6)
childprocess (0.7.0) childprocess (0.9.0)
ffi (~> 1.0, >= 1.0.11) ffi (~> 1.0, >= 1.0.11)
chronic (0.10.2) chronic (0.10.2)
chronic_duration (0.10.6) chronic_duration (0.10.6)
...@@ -517,7 +517,7 @@ GEM ...@@ -517,7 +517,7 @@ GEM
multi_json (~> 1.3) multi_json (~> 1.3)
multi_xml (~> 0.5) multi_xml (~> 0.5)
rack (>= 1.2, < 3) rack (>= 1.2, < 3)
octokit (4.8.0) octokit (4.9.0)
sawyer (~> 0.8.0, >= 0.5.3) sawyer (~> 0.8.0, >= 0.5.3)
omniauth (1.8.1) omniauth (1.8.1)
hashie (>= 3.4.6, < 3.6.0) hashie (>= 3.4.6, < 3.6.0)
...@@ -828,9 +828,9 @@ GEM ...@@ -828,9 +828,9 @@ GEM
activesupport (>= 3.1) activesupport (>= 3.1)
select2-rails (3.5.9.3) select2-rails (3.5.9.3)
thor (~> 0.14) thor (~> 0.14)
selenium-webdriver (3.5.0) selenium-webdriver (3.12.0)
childprocess (~> 0.5) childprocess (~> 0.5)
rubyzip (~> 1.0) rubyzip (~> 1.2)
sentry-raven (2.7.2) sentry-raven (2.7.2)
faraday (>= 0.7.6, < 1.0) faraday (>= 0.7.6, < 1.0)
settingslogic (2.0.9) settingslogic (2.0.9)
...@@ -1084,7 +1084,7 @@ DEPENDENCIES ...@@ -1084,7 +1084,7 @@ DEPENDENCIES
net-ssh (~> 4.2.0) net-ssh (~> 4.2.0)
nokogiri (~> 1.8.2) nokogiri (~> 1.8.2)
oauth2 (~> 1.4) oauth2 (~> 1.4)
octokit (~> 4.8) octokit (~> 4.9)
omniauth (~> 1.8) omniauth (~> 1.8)
omniauth-auth0 (~> 2.0.0) omniauth-auth0 (~> 2.0.0)
omniauth-authentiq (~> 0.3.3) omniauth-authentiq (~> 0.3.3)
...@@ -1154,7 +1154,7 @@ DEPENDENCIES ...@@ -1154,7 +1154,7 @@ DEPENDENCIES
scss_lint (~> 0.56.0) scss_lint (~> 0.56.0)
seed-fu (~> 2.3.7) seed-fu (~> 2.3.7)
select2-rails (~> 3.5.9) select2-rails (~> 3.5.9)
selenium-webdriver (~> 3.5) selenium-webdriver (~> 3.12)
sentry-raven (~> 2.7) sentry-raven (~> 2.7)
settingslogic (~> 2.0.9) settingslogic (~> 2.0.9)
sham_rack (~> 1.3.6) sham_rack (~> 1.3.6)
......
...@@ -54,7 +54,7 @@ export default { ...@@ -54,7 +54,7 @@ export default {
placement: 'top', placement: 'top',
content: sprintf( content: sprintf(
__(` __(`
The character highligher helps you keep the subject line to %{titleLength} characters The character highlighter helps you keep the subject line to %{titleLength} characters
and wrap the body at %{bodyLength} so they are readable in git. and wrap the body at %{bodyLength} so they are readable in git.
`), `),
{ titleLength: MAX_TITLE_LENGTH, bodyLength: MAX_BODY_LENGTH }, { titleLength: MAX_TITLE_LENGTH, bodyLength: MAX_BODY_LENGTH },
......
...@@ -25,14 +25,22 @@ module SearchHelper ...@@ -25,14 +25,22 @@ module SearchHelper
return unless collection.count > 0 return unless collection.count > 0
from = collection.offset_value + 1 from = collection.offset_value + 1
to = collection.offset_value + collection.length to = collection.offset_value + collection.count
count = collection.total_count count = collection.total_count
"Showing #{from} - #{to} of #{count} #{scope.humanize(capitalize: false)} for \"#{term}\"" "Showing #{from} - #{to} of #{count} #{scope.humanize(capitalize: false)} for \"#{term}\""
end end
def find_project_for_result_blob(result)
@project
end
def parse_search_result(result) def parse_search_result(result)
Gitlab::ProjectSearchResults.parse_search_result(result) result
end
def search_blob_title(project, filename)
filename
end end
private private
......
...@@ -7,12 +7,17 @@ module Ci ...@@ -7,12 +7,17 @@ module Ci
include Presentable include Presentable
include Gitlab::OptimisticLocking include Gitlab::OptimisticLocking
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
include AtomicInternalId
belongs_to :project, inverse_of: :pipelines belongs_to :project, inverse_of: :pipelines
belongs_to :user belongs_to :user
belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline' belongs_to :auto_canceled_by, class_name: 'Ci::Pipeline'
belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule' belongs_to :pipeline_schedule, class_name: 'Ci::PipelineSchedule'
has_internal_id :iid, scope: :project, presence: false, init: ->(s) do
s&.project&.pipelines&.maximum(:iid) || s&.project&.pipelines&.count
end
has_many :stages has_many :stages
has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline
has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline
...@@ -531,6 +536,7 @@ module Ci ...@@ -531,6 +536,7 @@ module Ci
def predefined_variables def predefined_variables
Gitlab::Ci::Variables::Collection.new Gitlab::Ci::Variables::Collection.new
.append(key: 'CI_PIPELINE_IID', value: iid.to_s)
.append(key: 'CI_CONFIG_PATH', value: ci_yaml_file_path) .append(key: 'CI_CONFIG_PATH', value: ci_yaml_file_path)
.append(key: 'CI_PIPELINE_SOURCE', value: source.to_s) .append(key: 'CI_PIPELINE_SOURCE', value: source.to_s)
.append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message) .append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message)
......
...@@ -11,12 +11,12 @@ module Clusters ...@@ -11,12 +11,12 @@ module Clusters
attr_encrypted :password, attr_encrypted :password,
mode: :per_attribute_iv, mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base, key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-cbc' algorithm: 'aes-256-cbc'
attr_encrypted :token, attr_encrypted :token,
mode: :per_attribute_iv, mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base, key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-cbc' algorithm: 'aes-256-cbc'
before_validation :enforce_namespace_to_lower_case before_validation :enforce_namespace_to_lower_case
......
...@@ -11,7 +11,7 @@ module Clusters ...@@ -11,7 +11,7 @@ module Clusters
attr_encrypted :access_token, attr_encrypted :access_token,
mode: :per_attribute_iv, mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base, key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-cbc' algorithm: 'aes-256-cbc'
validates :gcp_project_id, validates :gcp_project_id,
......
...@@ -25,9 +25,13 @@ module AtomicInternalId ...@@ -25,9 +25,13 @@ module AtomicInternalId
extend ActiveSupport::Concern extend ActiveSupport::Concern
module ClassMethods module ClassMethods
def has_internal_id(column, scope:, init:) # rubocop:disable Naming/PredicateName def has_internal_id(column, scope:, init:, presence: true) # rubocop:disable Naming/PredicateName
before_validation(on: :create) do before_validation :"ensure_#{scope}_#{column}!", on: :create
validates column, presence: presence
define_method("ensure_#{scope}_#{column}!") do
scope_value = association(scope).reader scope_value = association(scope).reader
if read_attribute(column).blank? && scope_value if read_attribute(column).blank? && scope_value
scope_attrs = { scope_value.class.table_name.singularize.to_sym => scope_value } scope_attrs = { scope_value.class.table_name.singularize.to_sym => scope_value }
usage = self.class.table_name.to_sym usage = self.class.table_name.to_sym
...@@ -35,13 +39,9 @@ module AtomicInternalId ...@@ -35,13 +39,9 @@ module AtomicInternalId
new_iid = InternalId.generate_next(self, scope_attrs, usage, init) new_iid = InternalId.generate_next(self, scope_attrs, usage, init)
write_attribute(column, new_iid) write_attribute(column, new_iid)
end end
end
validates column, presence: true, numericality: true read_attribute(column)
end end
end end
def to_param
iid.to_s
end end
end end
module IidRoutes
##
# This automagically enforces all related routes to use `iid` instead of `id`
# If you want to use `iid` for some routes and `id` for other routes, this module should not to be included,
# instead you should define `iid` or `id` explictly at each route generators. e.g. pipeline_path(project.id, pipeline.iid)
def to_param
iid.to_s
end
end
class Deployment < ActiveRecord::Base class Deployment < ActiveRecord::Base
include AtomicInternalId include AtomicInternalId
include IidRoutes
belongs_to :project, required: true belongs_to :project, required: true
belongs_to :environment, required: true belongs_to :environment, required: true
......
...@@ -14,7 +14,7 @@ class InternalId < ActiveRecord::Base ...@@ -14,7 +14,7 @@ class InternalId < ActiveRecord::Base
belongs_to :project belongs_to :project
belongs_to :namespace belongs_to :namespace
enum usage: { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4 } enum usage: { issues: 0, merge_requests: 1, deployments: 2, milestones: 3, epics: 4, ci_pipelines: 5 }
validates :usage, presence: true validates :usage, presence: true
......
...@@ -2,6 +2,7 @@ require 'carrierwave/orm/activerecord' ...@@ -2,6 +2,7 @@ require 'carrierwave/orm/activerecord'
class Issue < ActiveRecord::Base class Issue < ActiveRecord::Base
include AtomicInternalId include AtomicInternalId
include IidRoutes
include Issuable include Issuable
include Noteable include Noteable
include Referable include Referable
......
class MergeRequest < ActiveRecord::Base class MergeRequest < ActiveRecord::Base
include AtomicInternalId include AtomicInternalId
include IidRoutes
include Issuable include Issuable
include Noteable include Noteable
include Referable include Referable
......
...@@ -9,6 +9,7 @@ class Milestone < ActiveRecord::Base ...@@ -9,6 +9,7 @@ class Milestone < ActiveRecord::Base
include CacheMarkdownField include CacheMarkdownField
include AtomicInternalId include AtomicInternalId
include IidRoutes
include Sortable include Sortable
include Referable include Referable
include StripAttribute include StripAttribute
......
...@@ -140,10 +140,6 @@ class ProjectWiki ...@@ -140,10 +140,6 @@ class ProjectWiki
[title, title_array.join("/")] [title, title_array.join("/")]
end end
def search_files(query)
repository.search_files_by_content(query, default_branch)
end
def repository def repository
@repository ||= Repository.new(full_path, @project, disk_path: disk_path, is_wiki: true) @repository ||= Repository.new(full_path, @project, disk_path: disk_path, is_wiki: true)
end end
......
- file_name, blob = blob - project = find_project_for_result_blob(blob)
.blob-result - file_name, blob = parse_search_result(blob)
.file-holder - blob_link = project_blob_path(project, tree_join(blob.ref, file_name))
.js-file-title.file-title
- ref = @search_results.repository_ref = render partial: 'search/results/blob_data', locals: { blob: blob, project: project, file_name: file_name, blob_link: blob_link }
- blob_link = project_blob_path(@project, tree_join(ref, file_name))
= link_to blob_link do
%i.fa.fa-file
%strong
= file_name
- if blob
.file-content.code.term
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link
.blob-result
.file-holder
.js-file-title.file-title
= link_to blob_link do
%i.fa.fa-file
= search_blob_title(project, file_name)
- if blob.data
.file-content.code.term
= render 'shared/file_highlight', blob: blob, first_line_number: blob.startline
- wiki_blob = parse_search_result(wiki_blob) - project = find_project_for_result_blob(wiki_blob)
.blob-result - file_name, wiki_blob = parse_search_result(wiki_blob)
.file-holder - wiki_blob_link = project_wiki_path(project, wiki_blob.basename)
.js-file-title.file-title
= link_to project_wiki_path(@project, wiki_blob.basename) do = render partial: 'search/results/blob_data', locals: { blob: wiki_blob, project: project, file_name: file_name, blob_link: wiki_blob_link }
%i.fa.fa-file
%strong
= wiki_blob.basename
.file-content.code.term
= render 'shared/file_highlight', blob: wiki_blob, first_line_number: wiki_blob.startline
---
title: Update selenium-webdriver to 3.12.0
merge_request: 19351
author: Takuya Noguchi
type: other
---
title: Include username in output when testing SSH to GitLab
merge_request: 19358
author:
type: other
---
title: Added ability to search by wiki titles
merge_request: 19112
author:
type: added
---
title: Add per-project pipeline id
merge_request: 18558
author:
type: added
---
title: Rails5 fix arel from
merge_request: 19340
author: Jasper Maes
type: fixed
---
title: Fix attr_encryption key settings
merge_request:
author:
type: fixed
---
title: Fix N+1 with source_projects in merge requests API
merge_request:
author:
type: performance
...@@ -85,17 +85,24 @@ class Settings < Settingslogic ...@@ -85,17 +85,24 @@ class Settings < Settingslogic
File.expand_path(path, Rails.root) File.expand_path(path, Rails.root)
end end
# Returns a 256-bit key for attr_encrypted
def attr_encrypted_db_key_base
# Ruby 2.4+ requires passing in the exact required length for OpenSSL keys # Ruby 2.4+ requires passing in the exact required length for OpenSSL keys
# (https://github.com/ruby/ruby/commit/ce635262f53b760284d56bb1027baebaaec175d1). # (https://github.com/ruby/ruby/commit/ce635262f53b760284d56bb1027baebaaec175d1).
# Previous versions quietly truncated the input. # Previous versions quietly truncated the input.
# #
# The default mode for the attr_encrypted gem is to use a 256-bit key. # Use this when using :per_attribute_iv mode for attr_encrypted.
# We truncate the 128-byte string to 32 bytes. # We have to truncate the string to 32 bytes for a 256-bit cipher.
def attr_encrypted_db_key_base_truncated
Gitlab::Application.secrets.db_key_base[0..31] Gitlab::Application.secrets.db_key_base[0..31]
end end
# This should be used for :per_attribute_salt_and_iv mode. There is no
# need to truncate the key because the encryptor will use the salt to
# generate a hash of the password:
# https://github.com/attr-encrypted/encryptor/blob/c3a62c4a9e74686dd95e0548f9dc2a361fdc95d1/lib/encryptor.rb#L77
def attr_encrypted_db_key_base
Gitlab::Application.secrets.db_key_base
end
private private
def base_url(config) def base_url(config)
......
class AddPipelineIidToCiPipelines < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
add_column :ci_pipelines, :iid, :integer
end
def down
remove_column :ci_pipelines, :iid, :integer
end
end
class AddIndexConstraintsToPipelineIid < ActiveRecord::Migration
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :ci_pipelines, [:project_id, :iid], unique: true, where: 'iid IS NOT NULL'
end
def down
remove_concurrent_index :ci_pipelines, [:project_id, :iid]
end
end
...@@ -48,7 +48,7 @@ class MigrateKubernetesServiceToNewClustersArchitectures < ActiveRecord::Migrati ...@@ -48,7 +48,7 @@ class MigrateKubernetesServiceToNewClustersArchitectures < ActiveRecord::Migrati
attr_encrypted :token, attr_encrypted :token,
mode: :per_attribute_iv, mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base, key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-cbc' algorithm: 'aes-256-cbc'
end end
......
...@@ -451,10 +451,12 @@ ActiveRecord::Schema.define(version: 20180529093006) do ...@@ -451,10 +451,12 @@ ActiveRecord::Schema.define(version: 20180529093006) do
t.integer "config_source" t.integer "config_source"
t.boolean "protected" t.boolean "protected"
t.integer "failure_reason" t.integer "failure_reason"
t.integer "iid"
end end
add_index "ci_pipelines", ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree add_index "ci_pipelines", ["auto_canceled_by_id"], name: "index_ci_pipelines_on_auto_canceled_by_id", using: :btree
add_index "ci_pipelines", ["pipeline_schedule_id"], name: "index_ci_pipelines_on_pipeline_schedule_id", using: :btree add_index "ci_pipelines", ["pipeline_schedule_id"], name: "index_ci_pipelines_on_pipeline_schedule_id", using: :btree
add_index "ci_pipelines", ["project_id", "iid"], name: "index_ci_pipelines_on_project_id_and_iid", unique: true, where: "(iid IS NOT NULL)", using: :btree
add_index "ci_pipelines", ["project_id", "ref", "status", "id"], name: "index_ci_pipelines_on_project_id_and_ref_and_status_and_id", using: :btree add_index "ci_pipelines", ["project_id", "ref", "status", "id"], name: "index_ci_pipelines_on_project_id_and_ref_and_status_and_id", using: :btree
add_index "ci_pipelines", ["project_id", "sha"], name: "index_ci_pipelines_on_project_id_and_sha", using: :btree add_index "ci_pipelines", ["project_id", "sha"], name: "index_ci_pipelines_on_project_id_and_sha", using: :btree
add_index "ci_pipelines", ["project_id"], name: "index_ci_pipelines_on_project_id", using: :btree add_index "ci_pipelines", ["project_id"], name: "index_ci_pipelines_on_project_id", using: :btree
......
...@@ -83,12 +83,12 @@ you need to add a [wildcard DNS A record][wiki-wildcard-dns] pointing to the ...@@ -83,12 +83,12 @@ you need to add a [wildcard DNS A record][wiki-wildcard-dns] pointing to the
host that GitLab runs. For example, an entry would look like this: host that GitLab runs. For example, an entry would look like this:
``` ```
*.example.io. 1800 IN A 1.1.1.1 *.example.io. 1800 IN A 192.0.2.1
*.example.io. 1800 IN AAAA 2001::1 *.example.io. 1800 IN AAAA 2001::1
``` ```
where `example.io` is the domain under which GitLab Pages will be served where `example.io` is the domain under which GitLab Pages will be served
and `1.1.1.1` is the IPv4 address of your GitLab instance and `2001::1` is the and `192.0.2.1` is the IPv4 address of your GitLab instance and `2001::1` is the
IPv6 address. If you don't have IPv6, you can omit the AAAA record. IPv6 address. If you don't have IPv6, you can omit the AAAA record.
> **Note:** > **Note:**
...@@ -193,13 +193,13 @@ world. Custom domains are supported, but no TLS. ...@@ -193,13 +193,13 @@ world. Custom domains are supported, but no TLS.
```shell ```shell
pages_external_url "http://example.io" pages_external_url "http://example.io"
nginx['listen_addresses'] = ['1.1.1.1'] nginx['listen_addresses'] = ['192.0.2.1']
pages_nginx['enable'] = false pages_nginx['enable'] = false
gitlab_pages['external_http'] = ['1.1.1.2:80', '[2001::2]:80'] gitlab_pages['external_http'] = ['192.0.2.2:80', '[2001::2]:80']
``` ```
where `1.1.1.1` is the primary IP address that GitLab is listening to and where `192.0.2.1` is the primary IP address that GitLab is listening to and
`1.1.1.2` and `2001::2` are the secondary IPs the GitLab Pages daemon `192.0.2.2` and `2001::2` are the secondary IPs the GitLab Pages daemon
listens on. If you don't have IPv6, you can omit the IPv6 address. listens on. If you don't have IPv6, you can omit the IPv6 address.
1. [Reconfigure GitLab][reconfigure] 1. [Reconfigure GitLab][reconfigure]
...@@ -228,16 +228,16 @@ world. Custom domains and TLS are supported. ...@@ -228,16 +228,16 @@ world. Custom domains and TLS are supported.
```shell ```shell
pages_external_url "https://example.io" pages_external_url "https://example.io"
nginx['listen_addresses'] = ['1.1.1.1'] nginx['listen_addresses'] = ['192.0.2.1']
pages_nginx['enable'] = false pages_nginx['enable'] = false
gitlab_pages['cert'] = "/etc/gitlab/ssl/example.io.crt" gitlab_pages['cert'] = "/etc/gitlab/ssl/example.io.crt"
gitlab_pages['cert_key'] = "/etc/gitlab/ssl/example.io.key" gitlab_pages['cert_key'] = "/etc/gitlab/ssl/example.io.key"
gitlab_pages['external_http'] = ['1.1.1.2:80', '[2001::2]:80'] gitlab_pages['external_http'] = ['192.0.2.2:80', '[2001::2]:80']
gitlab_pages['external_https'] = ['1.1.1.2:443', '[2001::2]:443'] gitlab_pages['external_https'] = ['192.0.2.2:443', '[2001::2]:443']
``` ```
where `1.1.1.1` is the primary IP address that GitLab is listening to and where `192.0.2.1` is the primary IP address that GitLab is listening to and
`1.1.1.2` and `2001::2` are the secondary IPs where the GitLab Pages daemon `192.0.2.2` and `2001::2` are the secondary IPs where the GitLab Pages daemon
listens on. If you don't have IPv6, you can omit the IPv6 address. listens on. If you don't have IPv6, you can omit the IPv6 address.
1. [Reconfigure GitLab][reconfigure] 1. [Reconfigure GitLab][reconfigure]
......
...@@ -67,11 +67,11 @@ you need to add a [wildcard DNS A record][wiki-wildcard-dns] pointing to the ...@@ -67,11 +67,11 @@ you need to add a [wildcard DNS A record][wiki-wildcard-dns] pointing to the
host that GitLab runs. For example, an entry would look like this: host that GitLab runs. For example, an entry would look like this:
``` ```
*.example.io. 1800 IN A 1.1.1.1 *.example.io. 1800 IN A 192.0.2.1
``` ```
where `example.io` is the domain under which GitLab Pages will be served where `example.io` is the domain under which GitLab Pages will be served
and `1.1.1.1` is the IP address of your GitLab instance. and `192.0.2.1` is the IP address of your GitLab instance.
> **Note:** > **Note:**
You should not use the GitLab domain to serve user pages. For more information You should not use the GitLab domain to serve user pages. For more information
...@@ -253,7 +253,7 @@ world. Custom domains are supported, but no TLS. ...@@ -253,7 +253,7 @@ world. Custom domains are supported, but no TLS.
port: 80 port: 80
https: false https: false
external_http: 1.1.1.2:80 external_http: 192.0.2.2:80
``` ```
1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in 1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in
...@@ -263,7 +263,7 @@ world. Custom domains are supported, but no TLS. ...@@ -263,7 +263,7 @@ world. Custom domains are supported, but no TLS.
``` ```
gitlab_pages_enabled=true gitlab_pages_enabled=true
gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -listen-http 1.1.1.2:80" gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -listen-http 192.0.2.2:80"
``` ```
1. Copy the `gitlab-pages-ssl` Nginx configuration file: 1. Copy the `gitlab-pages-ssl` Nginx configuration file:
...@@ -274,7 +274,7 @@ world. Custom domains are supported, but no TLS. ...@@ -274,7 +274,7 @@ world. Custom domains are supported, but no TLS.
``` ```
1. Edit all GitLab related configs in `/etc/nginx/site-available/` and replace 1. Edit all GitLab related configs in `/etc/nginx/site-available/` and replace
`0.0.0.0` with `1.1.1.1`, where `1.1.1.1` the primary IP where GitLab `0.0.0.0` with `192.0.2.1`, where `192.0.2.1` the primary IP where GitLab
listens to. listens to.
1. Restart NGINX 1. Restart NGINX
1. [Restart GitLab][restart] 1. [Restart GitLab][restart]
...@@ -320,8 +320,8 @@ world. Custom domains and TLS are supported. ...@@ -320,8 +320,8 @@ world. Custom domains and TLS are supported.
port: 443 port: 443
https: true https: true
external_http: 1.1.1.2:80 external_http: 192.0.2.2:80
external_https: 1.1.1.2:443 external_https: 192.0.2.2:443
``` ```
1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in 1. Edit `/etc/default/gitlab` and set `gitlab_pages_enabled` to `true` in
...@@ -333,7 +333,7 @@ world. Custom domains and TLS are supported. ...@@ -333,7 +333,7 @@ world. Custom domains and TLS are supported.
``` ```
gitlab_pages_enabled=true gitlab_pages_enabled=true
gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -listen-http 1.1.1.2:80 -listen-https 1.1.1.2:443 -root-cert /path/to/example.io.crt -root-key /path/to/example.io.key gitlab_pages_options="-pages-domain example.io -pages-root $app_root/shared/pages -listen-proxy 127.0.0.1:8090 -listen-http 192.0.2.2:80 -listen-https 192.0.2.2:443 -root-cert /path/to/example.io.crt -root-key /path/to/example.io.key
``` ```
1. Copy the `gitlab-pages-ssl` Nginx configuration file: 1. Copy the `gitlab-pages-ssl` Nginx configuration file:
...@@ -344,7 +344,7 @@ world. Custom domains and TLS are supported. ...@@ -344,7 +344,7 @@ world. Custom domains and TLS are supported.
``` ```
1. Edit all GitLab related configs in `/etc/nginx/site-available/` and replace 1. Edit all GitLab related configs in `/etc/nginx/site-available/` and replace
`0.0.0.0` with `1.1.1.1`, where `1.1.1.1` the primary IP where GitLab `0.0.0.0` with `192.0.2.1`, where `192.0.2.1` the primary IP where GitLab
listens to. listens to.
1. Restart NGINX 1. Restart NGINX
1. [Restart GitLab][restart] 1. [Restart GitLab][restart]
......
...@@ -582,6 +582,7 @@ Parameters: ...@@ -582,6 +582,7 @@ Parameters:
"changes_count": "1", "changes_count": "1",
"should_remove_source_branch": true, "should_remove_source_branch": true,
"force_remove_source_branch": false, "force_remove_source_branch": false,
"squash": false,
"web_url": "http://example.com/example/example/merge_requests/1", "web_url": "http://example.com/example/example/merge_requests/1",
"discussion_locked": false, "discussion_locked": false,
"time_stats": { "time_stats": {
......
...@@ -72,6 +72,7 @@ future GitLab releases.** ...@@ -72,6 +72,7 @@ future GitLab releases.**
| **CI_RUNNER_REVISION** | all | 10.6 | GitLab Runner revision that is executing the current job | | **CI_RUNNER_REVISION** | all | 10.6 | GitLab Runner revision that is executing the current job |
| **CI_RUNNER_EXECUTABLE_ARCH** | all | 10.6 | The OS/architecture of the GitLab Runner executable (note that this is not necessarily the same as the environment of the executor) | | **CI_RUNNER_EXECUTABLE_ARCH** | all | 10.6 | The OS/architecture of the GitLab Runner executable (note that this is not necessarily the same as the environment of the executor) |
| **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally | | **CI_PIPELINE_ID** | 8.10 | 0.5 | The unique id of the current pipeline that GitLab CI uses internally |
| **CI_PIPELINE_IID** | 11.0 | all | The unique id of the current pipeline scoped to project |
| **CI_PIPELINE_TRIGGERED** | all | all | The flag to indicate that job was [triggered] | | **CI_PIPELINE_TRIGGERED** | all | all | The flag to indicate that job was [triggered] |
| **CI_PIPELINE_SOURCE** | 10.0 | all | Indicates how the pipeline was triggered. Possible options are: `push`, `web`, `trigger`, `schedule`, `api`, and `pipeline`. For pipelines created before GitLab 9.5, this will show as `unknown` | | **CI_PIPELINE_SOURCE** | 10.0 | all | Indicates how the pipeline was triggered. Possible options are: `push`, `web`, `trigger`, `schedule`, `api`, and `pipeline`. For pipelines created before GitLab 9.5, this will show as `unknown` |
| **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the job is run | | **CI_PROJECT_DIR** | all | all | The full path where the repository is cloned and where the job is run |
...@@ -352,6 +353,8 @@ Running on runner-8a2f473d-project-1796893-concurrent-0 via runner-8a2f473d-mach ...@@ -352,6 +353,8 @@ Running on runner-8a2f473d-project-1796893-concurrent-0 via runner-8a2f473d-mach
++ CI_PROJECT_URL=https://example.com/gitlab-examples/ci-debug-trace ++ CI_PROJECT_URL=https://example.com/gitlab-examples/ci-debug-trace
++ export CI_PIPELINE_ID=52666 ++ export CI_PIPELINE_ID=52666
++ CI_PIPELINE_ID=52666 ++ CI_PIPELINE_ID=52666
++ export CI_PIPELINE_IID=123
++ CI_PIPELINE_IID=123
++ export CI_RUNNER_ID=1337 ++ export CI_RUNNER_ID=1337
++ CI_RUNNER_ID=1337 ++ CI_RUNNER_ID=1337
++ export CI_RUNNER_DESCRIPTION=shared-runners-manager-1.example.com ++ export CI_RUNNER_DESCRIPTION=shared-runners-manager-1.example.com
...@@ -439,6 +442,7 @@ export CI_JOB_MANUAL="true" ...@@ -439,6 +442,7 @@ export CI_JOB_MANUAL="true"
export CI_JOB_TRIGGERED="true" export CI_JOB_TRIGGERED="true"
export CI_JOB_TOKEN="abcde-1234ABCD5678ef" export CI_JOB_TOKEN="abcde-1234ABCD5678ef"
export CI_PIPELINE_ID="1000" export CI_PIPELINE_ID="1000"
export CI_PIPELINE_IID="10"
export CI_PROJECT_ID="34" export CI_PROJECT_ID="34"
export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-ce" export CI_PROJECT_DIR="/builds/gitlab-org/gitlab-ce"
export CI_PROJECT_NAME="gitlab-ce" export CI_PROJECT_NAME="gitlab-ce"
......
...@@ -144,7 +144,7 @@ helm install --name gitlab -f values.yaml gitlab/gitlab-omnibus ...@@ -144,7 +144,7 @@ helm install --name gitlab -f values.yaml gitlab/gitlab-omnibus
or passing them on the command line: or passing them on the command line:
```bash ```bash
helm install --name gitlab --set baseDomain=gitlab.io,baseIP=1.1.1.1,gitlab=ee,gitlabEELicense=$LICENSE,legoEmail=email@gitlab.com gitlab/gitlab-omnibus helm install --name gitlab --set baseDomain=gitlab.io,baseIP=192.0.2.1,gitlab=ee,gitlabEELicense=$LICENSE,legoEmail=email@gitlab.com gitlab/gitlab-omnibus
``` ```
## Updating GitLab using the Helm Chart ## Updating GitLab using the Helm Chart
......
...@@ -38,7 +38,7 @@ module API ...@@ -38,7 +38,7 @@ module API
merge_requests = MergeRequestsFinder.new(current_user, args).execute merge_requests = MergeRequestsFinder.new(current_user, args).execute
.reorder(args[:order_by] => args[:sort]) .reorder(args[:order_by] => args[:sort])
merge_requests = paginate(merge_requests) merge_requests = paginate(merge_requests)
.preload(:target_project) .preload(:source_project, :target_project)
return merge_requests if args[:view] == 'simple' return merge_requests if args[:view] == 'simple'
......
...@@ -34,9 +34,7 @@ module API ...@@ -34,9 +34,7 @@ module API
def process_results(results) def process_results(results)
case params[:scope] case params[:scope]
when 'wiki_blobs' when 'blobs', 'wiki_blobs'
paginate(results).map { |blob| Gitlab::ProjectSearchResults.parse_search_result(blob, user_project) }
when 'blobs'
paginate(results).map { |blob| blob[1] } paginate(results).map { |blob| blob[1] }
else else
paginate(results) paginate(results)
......
...@@ -8,6 +8,9 @@ module Gitlab ...@@ -8,6 +8,9 @@ module Gitlab
PopulateError = Class.new(StandardError) PopulateError = Class.new(StandardError)
def perform! def perform!
# Allocate next IID. This operation must be outside of transactions of pipeline creations.
pipeline.ensure_project_iid!
## ##
# Populate pipeline with block argument of CreatePipelineService#execute. # Populate pipeline with block argument of CreatePipelineService#execute.
# #
......
...@@ -143,8 +143,13 @@ module Gitlab ...@@ -143,8 +143,13 @@ module Gitlab
.order(arel_table[column_sym]) .order(arel_table[column_sym])
).as('row_id') ).as('row_id')
count = arel_table.from(arel_table.alias) arel_from = if Gitlab.rails5?
.project('COUNT(*)') arel_table.from.from(arel_table.alias)
else
arel_table.from(arel_table.alias)
end
count = arel_from.project('COUNT(*)')
.where(arel_table[partition_column].eq(arel_table.alias[partition_column])) .where(arel_table[partition_column].eq(arel_table.alias[partition_column]))
.as('ct') .as('ct')
......
...@@ -32,17 +32,13 @@ module Gitlab ...@@ -32,17 +32,13 @@ module Gitlab
end end
def find_by_filename(query, except: []) def find_by_filename(query, except: [])
filenames = repository.search_files_by_name(query, ref).first(BATCH_SIZE) filenames = search_filenames(query, except)
filenames.delete_if { |filename| except.include?(filename) } unless except.empty?
blob_refs = filenames.map { |filename| [ref, filename] } blobs(filenames).map do |blob|
blobs = Gitlab::Git::Blob.batch(repository, blob_refs, blob_size_limit: 1024)
blobs.map do |blob|
Gitlab::SearchResults::FoundBlob.new( Gitlab::SearchResults::FoundBlob.new(
id: blob.id, id: blob.id,
filename: blob.path, filename: blob.path,
basename: File.basename(blob.path), basename: File.basename(blob.path, File.extname(blob.path)),
ref: ref, ref: ref,
startline: 1, startline: 1,
data: blob.data, data: blob.data,
...@@ -50,5 +46,21 @@ module Gitlab ...@@ -50,5 +46,21 @@ module Gitlab
) )
end end
end end
def search_filenames(query, except)
filenames = repository.search_files_by_name(query, ref).first(BATCH_SIZE)
filenames.delete_if { |filename| except.include?(filename) } unless except.empty?
filenames
end
def blob_refs(filenames)
filenames.map { |filename| [ref, filename] }
end
def blobs(filenames)
Gitlab::Git::Blob.batch(repository, blob_refs(filenames), blob_size_limit: 1024)
end
end end
end end
...@@ -106,7 +106,8 @@ module Gitlab ...@@ -106,7 +106,8 @@ module Gitlab
project_wiki = ProjectWiki.new(project) project_wiki = ProjectWiki.new(project)
unless project_wiki.empty? unless project_wiki.empty?
project_wiki.search_files(query) ref = repository_ref || project.wiki.default_branch
Gitlab::WikiFileFinder.new(project, ref).find(query)
else else
[] []
end end
......
module Gitlab module Gitlab
module SlashCommands module SlashCommands
class Command < BaseCommand class Command < BaseCommand
COMMANDS = [ def self.commands
[
Gitlab::SlashCommands::IssueShow, Gitlab::SlashCommands::IssueShow,
Gitlab::SlashCommands::IssueNew, Gitlab::SlashCommands::IssueNew,
Gitlab::SlashCommands::IssueSearch, Gitlab::SlashCommands::IssueSearch,
Gitlab::SlashCommands::IssueMove, Gitlab::SlashCommands::IssueMove,
Gitlab::SlashCommands::Deploy Gitlab::SlashCommands::Deploy
].freeze ]
end
def execute def execute
command, match = match_command command, match = match_command
...@@ -37,7 +39,7 @@ module Gitlab ...@@ -37,7 +39,7 @@ module Gitlab
private private
def available_commands def available_commands
COMMANDS.select do |klass| self.class.commands.keep_if do |klass|
klass.available?(project) klass.available?(project)
end end
end end
......
module Gitlab
class WikiFileFinder < FileFinder
attr_reader :repository
def initialize(project, ref)
@project = project
@ref = ref
@repository = project.wiki.repository
end
private
def search_filenames(query, except)
safe_query = Regexp.escape(query.tr(' ', '-'))
safe_query = Regexp.new(safe_query, Regexp::IGNORECASE)
filenames = repository.ls_files(ref)
filenames.delete_if { |filename| except.include?(filename) } unless except.empty?
filenames.grep(safe_query).first(BATCH_SIZE)
end
end
end
...@@ -42,6 +42,10 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do ...@@ -42,6 +42,10 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
it 'correctly assigns user' do it 'correctly assigns user' do
expect(pipeline.builds).to all(have_attributes(user: user)) expect(pipeline.builds).to all(have_attributes(user: user))
end end
it 'has pipeline iid' do
expect(pipeline.iid).to be > 0
end
end end
context 'when pipeline is empty' do context 'when pipeline is empty' do
...@@ -68,6 +72,10 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do ...@@ -68,6 +72,10 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
expect(pipeline.errors.to_a) expect(pipeline.errors.to_a)
.to include 'No stages / jobs for this pipeline.' .to include 'No stages / jobs for this pipeline.'
end end
it 'wastes pipeline iid' do
expect(InternalId.ci_pipelines.where(project_id: project.id).last.last_value).to be > 0
end
end end
context 'when pipeline has validation errors' do context 'when pipeline has validation errors' do
...@@ -87,6 +95,10 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do ...@@ -87,6 +95,10 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
expect(pipeline.errors.to_a) expect(pipeline.errors.to_a)
.to include 'Failed to build the pipeline!' .to include 'Failed to build the pipeline!'
end end
it 'wastes pipeline iid' do
expect(InternalId.ci_pipelines.where(project_id: project.id).last.last_value).to be > 0
end
end end
context 'when there is a seed blocks present' do context 'when there is a seed blocks present' do
...@@ -111,6 +123,12 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do ...@@ -111,6 +123,12 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
expect(pipeline.variables.first.key).to eq 'VAR' expect(pipeline.variables.first.key).to eq 'VAR'
expect(pipeline.variables.first.value).to eq '123' expect(pipeline.variables.first.value).to eq '123'
end end
it 'has pipeline iid' do
step.perform!
expect(pipeline.iid).to be > 0
end
end end
context 'when seeds block tries to persist some resources' do context 'when seeds block tries to persist some resources' do
...@@ -121,6 +139,12 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do ...@@ -121,6 +139,12 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
it 'raises exception' do it 'raises exception' do
expect { step.perform! }.to raise_error(ActiveRecord::RecordNotSaved) expect { step.perform! }.to raise_error(ActiveRecord::RecordNotSaved)
end end
it 'wastes pipeline iid' do
expect { step.perform! }.to raise_error
expect(InternalId.ci_pipelines.where(project_id: project.id).last.last_value).to be > 0
end
end end
end end
...@@ -132,6 +156,17 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do ...@@ -132,6 +156,17 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
end end
end end
context 'when variables policy is specified' do
shared_examples_for 'a correct pipeline' do
it 'populates pipeline according to used policies' do
step.perform!
expect(pipeline.stages.size).to eq 1
expect(pipeline.stages.first.builds.size).to eq 1
expect(pipeline.stages.first.builds.first.name).to eq 'rspec'
end
end
context 'when using only/except build policies' do context 'when using only/except build policies' do
let(:config) do let(:config) do
{ rspec: { script: 'rspec', stage: 'test', only: ['master'] }, { rspec: { script: 'rspec', stage: 'test', only: ['master'] },
...@@ -142,12 +177,18 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do ...@@ -142,12 +177,18 @@ describe Gitlab::Ci::Pipeline::Chain::Populate do
build(:ci_pipeline, ref: 'master', config: config) build(:ci_pipeline, ref: 'master', config: config)
end end
it 'populates pipeline according to used policies' do it_behaves_like 'a correct pipeline'
step.perform!
expect(pipeline.stages.size).to eq 1 context 'when variables expression is specified' do
expect(pipeline.stages.first.builds.size).to eq 1 context 'when pipeline iid is the subject' do
expect(pipeline.stages.first.builds.first.name).to eq 'rspec' let(:config) do
{ rspec: { script: 'rspec', only: { variables: ["$CI_PIPELINE_IID == '1'"] } },
prod: { script: 'cap prod', only: { variables: ["$CI_PIPELINE_IID == '1000'"] } } }
end
it_behaves_like 'a correct pipeline'
end
end
end end
end end
end end
...@@ -3,7 +3,12 @@ require 'spec_helper' ...@@ -3,7 +3,12 @@ require 'spec_helper'
describe Gitlab::CycleAnalytics::UsageData do describe Gitlab::CycleAnalytics::UsageData do
describe '#to_json' do describe '#to_json' do
before do before do
Timecop.freeze do # Since git commits only have second precision, round up to the
# nearest second to ensure we have accurate median and standard
# deviation calculations.
current_time = Time.at(Time.now.to_i)
Timecop.freeze(current_time) do
user = create(:user, :admin) user = create(:user, :admin)
projects = create_list(:project, 2, :repository) projects = create_list(:project, 2, :repository)
...@@ -37,13 +42,7 @@ describe Gitlab::CycleAnalytics::UsageData do ...@@ -37,13 +42,7 @@ describe Gitlab::CycleAnalytics::UsageData do
expected_values.each_pair do |op, value| expected_values.each_pair do |op, value|
expect(stage_values).to have_key(op) expect(stage_values).to have_key(op)
if op == :missing
expect(stage_values[op]).to eq(value) expect(stage_values[op]).to eq(value)
else
# delta is used because of git timings that Timecop does not stub
expect(stage_values[op].to_i).to be_within(5).of(value.to_i)
end
end end
end end
end end
...@@ -58,8 +57,8 @@ describe Gitlab::CycleAnalytics::UsageData do ...@@ -58,8 +57,8 @@ describe Gitlab::CycleAnalytics::UsageData do
missing: 0 missing: 0
}, },
plan: { plan: {
average: 2, average: 1,
sd: 2, sd: 0,
missing: 0 missing: 0
}, },
code: { code: {
......
...@@ -3,27 +3,11 @@ require 'spec_helper' ...@@ -3,27 +3,11 @@ require 'spec_helper'
describe Gitlab::FileFinder do describe Gitlab::FileFinder do
describe '#find' do describe '#find' do
let(:project) { create(:project, :public, :repository) } let(:project) { create(:project, :public, :repository) }
let(:finder) { described_class.new(project, project.default_branch) }
it 'finds by name' do it_behaves_like 'file finder' do
results = finder.find('files') subject { described_class.new(project, project.default_branch) }
let(:expected_file_by_name) { 'files/images/wm.svg' }
filename, blob = results.find { |_, blob| blob.filename == 'files/images/wm.svg' } let(:expected_file_by_content) { 'CHANGELOG' }
expect(filename).to eq('files/images/wm.svg')
expect(blob).to be_a(Gitlab::SearchResults::FoundBlob)
expect(blob.ref).to eq(finder.ref)
expect(blob.data).not_to be_empty
end
it 'finds by content' do
results = finder.find('files')
filename, blob = results.find { |_, blob| blob.filename == 'CHANGELOG' }
expect(filename).to eq('CHANGELOG')
expect(blob).to be_a(Gitlab::SearchResults::FoundBlob)
expect(blob.ref).to eq(finder.ref)
expect(blob.data).not_to be_empty
end end
end end
end end
...@@ -242,6 +242,7 @@ Ci::Pipeline: ...@@ -242,6 +242,7 @@ Ci::Pipeline:
- config_source - config_source
- failure_reason - failure_reason
- protected - protected
- iid
Ci::Stage: Ci::Stage:
- id - id
- name - name
......
...@@ -22,47 +22,57 @@ describe Gitlab::ProjectSearchResults do ...@@ -22,47 +22,57 @@ describe Gitlab::ProjectSearchResults do
it { expect(results.query).to eq('hello world') } it { expect(results.query).to eq('hello world') }
end end
describe 'blob search' do shared_examples 'general blob search' do |entity_type, blob_kind|
let(:project) { create(:project, :public, :repository) } let(:query) { 'files' }
subject(:results) { described_class.new(user, project, query).objects(blob_type) }
subject(:results) { described_class.new(user, project, 'files').objects('blobs') }
context 'when repository is disabled' do
let(:project) { create(:project, :public, :repository, :repository_disabled) }
it 'hides blobs from members' do context "when #{entity_type} is disabled" do
let(:project) { disabled_project }
it "hides #{blob_kind} from members" do
project.add_reporter(user) project.add_reporter(user)
is_expected.to be_empty is_expected.to be_empty
end end
it 'hides blobs from non-members' do it "hides #{blob_kind} from non-members" do
is_expected.to be_empty is_expected.to be_empty
end end
end end
context 'when repository is internal' do context "when #{entity_type} is internal" do
let(:project) { create(:project, :public, :repository, :repository_private) } let(:project) { private_project }
it 'finds blobs for members' do it "finds #{blob_kind} for members" do
project.add_reporter(user) project.add_reporter(user)
is_expected.not_to be_empty is_expected.not_to be_empty
end end
it 'hides blobs from non-members' do it "hides #{blob_kind} from non-members" do
is_expected.to be_empty is_expected.to be_empty
end end
end end
it 'finds by name' do it 'finds by name' do
expect(results.map(&:first)).to include('files/images/wm.svg') expect(results.map(&:first)).to include(expected_file_by_name)
end end
it 'finds by content' do it 'finds by content' do
blob = results.select { |result| result.first == "CHANGELOG" }.flatten.last blob = results.select { |result| result.first == expected_file_by_content }.flatten.last
expect(blob.filename).to eq("CHANGELOG") expect(blob.filename).to eq(expected_file_by_content)
end
end
describe 'blob search' do
let(:project) { create(:project, :public, :repository) }
it_behaves_like 'general blob search', 'repository', 'blobs' do
let(:blob_type) { 'blobs' }
let(:disabled_project) { create(:project, :public, :repository, :repository_disabled) }
let(:private_project) { create(:project, :public, :repository, :repository_private) }
let(:expected_file_by_name) { 'files/images/wm.svg' }
let(:expected_file_by_content) { 'CHANGELOG' }
end end
describe 'parsing results' do describe 'parsing results' do
...@@ -189,40 +199,18 @@ describe Gitlab::ProjectSearchResults do ...@@ -189,40 +199,18 @@ describe Gitlab::ProjectSearchResults do
describe 'wiki search' do describe 'wiki search' do
let(:project) { create(:project, :public, :wiki_repo) } let(:project) { create(:project, :public, :wiki_repo) }
let(:wiki) { build(:project_wiki, project: project) } let(:wiki) { build(:project_wiki, project: project) }
let!(:wiki_page) { wiki.create_page('Title', 'Content') }
subject(:results) { described_class.new(user, project, 'Content').objects('wiki_blobs') }
context 'when wiki is disabled' do
let(:project) { create(:project, :public, :wiki_repo, :wiki_disabled) }
it 'hides wiki blobs from members' do
project.add_reporter(user)
is_expected.to be_empty
end
it 'hides wiki blobs from non-members' do
is_expected.to be_empty
end
end
context 'when wiki is internal' do
let(:project) { create(:project, :public, :wiki_repo, :wiki_private) }
it 'finds wiki blobs for guest' do
project.add_guest(user)
is_expected.not_to be_empty before do
end wiki.create_page('Files/Title', 'Content')
wiki.create_page('CHANGELOG', 'Files example')
it 'hides wiki blobs from non-members' do
is_expected.to be_empty
end
end end
it 'finds by content' do it_behaves_like 'general blob search', 'wiki', 'wiki blobs' do
expect(results).to include("master:Title.md\x001\x00Content\n") let(:blob_type) { 'wiki_blobs' }
let(:disabled_project) { create(:project, :public, :wiki_repo, :wiki_disabled) }
let(:private_project) { create(:project, :public, :wiki_repo, :wiki_private) }
let(:expected_file_by_name) { 'Files/Title.md' }
let(:expected_file_by_content) { 'CHANGELOG.md' }
end end
end end
......
require 'spec_helper'
describe Gitlab::WikiFileFinder do
describe '#find' do
let(:project) { create(:project, :public, :wiki_repo) }
let(:wiki) { build(:project_wiki, project: project) }
before do
wiki.create_page('Files/Title', 'Content')
wiki.create_page('CHANGELOG', 'Files example')
end
it_behaves_like 'file finder' do
subject { described_class.new(project, project.wiki.default_branch) }
let(:expected_file_by_name) { 'Files/Title.md' }
let(:expected_file_by_content) { 'CHANGELOG.md' }
end
end
end
...@@ -1559,6 +1559,7 @@ describe Ci::Build do ...@@ -1559,6 +1559,7 @@ describe Ci::Build do
{ key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true }, { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true },
{ key: 'CI_PROJECT_URL', value: project.web_url, public: true }, { key: 'CI_PROJECT_URL', value: project.web_url, public: true },
{ key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true }, { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true },
{ key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true },
{ key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true }, { key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true },
{ key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true }, { key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true },
{ key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true }, { key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true },
......
...@@ -35,6 +35,16 @@ describe Ci::Pipeline, :mailer do ...@@ -35,6 +35,16 @@ describe Ci::Pipeline, :mailer do
end end
end end
describe 'modules' do
it_behaves_like 'AtomicInternalId', validate_presence: false do
let(:internal_id_attribute) { :iid }
let(:instance) { build(:ci_pipeline) }
let(:scope) { :project }
let(:scope_attrs) { { project: instance.project } }
let(:usage) { :ci_pipelines }
end
end
describe '#source' do describe '#source' do
context 'when creating new pipeline' do context 'when creating new pipeline' do
let(:pipeline) do let(:pipeline) do
...@@ -195,7 +205,8 @@ describe Ci::Pipeline, :mailer do ...@@ -195,7 +205,8 @@ describe Ci::Pipeline, :mailer do
it 'includes all predefined variables in a valid order' do it 'includes all predefined variables in a valid order' do
keys = subject.map { |variable| variable[:key] } keys = subject.map { |variable| variable[:key] }
expect(keys).to eq %w[CI_CONFIG_PATH expect(keys).to eq %w[CI_PIPELINE_IID
CI_CONFIG_PATH
CI_PIPELINE_SOURCE CI_PIPELINE_SOURCE
CI_COMMIT_MESSAGE CI_COMMIT_MESSAGE
CI_COMMIT_TITLE CI_COMMIT_TITLE
......
...@@ -20,6 +20,7 @@ describe Deployment do ...@@ -20,6 +20,7 @@ describe Deployment do
it_behaves_like 'AtomicInternalId' do it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid } let(:internal_id_attribute) { :iid }
let(:instance) { build(:deployment) } let(:instance) { build(:deployment) }
let(:scope) { :project }
let(:scope_attrs) { { project: instance.project } } let(:scope_attrs) { { project: instance.project } }
let(:usage) { :deployments } let(:usage) { :deployments }
end end
......
...@@ -17,6 +17,7 @@ describe Issue do ...@@ -17,6 +17,7 @@ describe Issue do
it_behaves_like 'AtomicInternalId' do it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid } let(:internal_id_attribute) { :iid }
let(:instance) { build(:issue) } let(:instance) { build(:issue) }
let(:scope) { :project }
let(:scope_attrs) { { project: instance.project } } let(:scope_attrs) { { project: instance.project } }
let(:usage) { :issues } let(:usage) { :issues }
end end
......
...@@ -84,6 +84,7 @@ describe MergeRequest do ...@@ -84,6 +84,7 @@ describe MergeRequest do
it_behaves_like 'AtomicInternalId' do it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid } let(:internal_id_attribute) { :iid }
let(:instance) { build(:merge_request) } let(:instance) { build(:merge_request) }
let(:scope) { :target_project }
let(:scope_attrs) { { project: instance.target_project } } let(:scope_attrs) { { project: instance.target_project } }
let(:usage) { :merge_requests } let(:usage) { :merge_requests }
end end
......
...@@ -6,6 +6,7 @@ describe Milestone do ...@@ -6,6 +6,7 @@ describe Milestone do
it_behaves_like 'AtomicInternalId' do it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid } let(:internal_id_attribute) { :iid }
let(:instance) { build(:milestone, project: build(:project), group: nil) } let(:instance) { build(:milestone, project: build(:project), group: nil) }
let(:scope) { :project }
let(:scope_attrs) { { project: instance.project } } let(:scope_attrs) { { project: instance.project } }
let(:usage) { :milestones } let(:usage) { :milestones }
end end
...@@ -15,6 +16,7 @@ describe Milestone do ...@@ -15,6 +16,7 @@ describe Milestone do
it_behaves_like 'AtomicInternalId' do it_behaves_like 'AtomicInternalId' do
let(:internal_id_attribute) { :iid } let(:internal_id_attribute) { :iid }
let(:instance) { build(:milestone, project: nil, group: build(:group)) } let(:instance) { build(:milestone, project: nil, group: build(:group)) }
let(:scope) { :group }
let(:scope_attrs) { { namespace: instance.group } } let(:scope_attrs) { { namespace: instance.group } }
let(:usage) { :milestones } let(:usage) { :milestones }
end end
......
...@@ -4,12 +4,12 @@ module CycleAnalyticsHelpers ...@@ -4,12 +4,12 @@ module CycleAnalyticsHelpers
create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name) create_commit("Commit for ##{issue.iid}", issue.project, user, branch_name)
end end
def create_commit(message, project, user, branch_name, count: 1) def create_commit(message, project, user, branch_name, count: 1, commit_time: nil, skip_push_handler: false)
repository = project.repository repository = project.repository
oldrev = repository.commit(branch_name).sha oldrev = repository.commit(branch_name)&.sha || Gitlab::Git::BLANK_SHA
if Timecop.frozen? && Gitlab::GitalyClient.feature_enabled?(:operation_user_commit_files) if Timecop.frozen? && Gitlab::GitalyClient.feature_enabled?(:operation_user_commit_files)
mock_gitaly_multi_action_dates(repository.raw) mock_gitaly_multi_action_dates(repository.raw, commit_time)
end end
commit_shas = Array.new(count) do |index| commit_shas = Array.new(count) do |index|
...@@ -19,6 +19,8 @@ module CycleAnalyticsHelpers ...@@ -19,6 +19,8 @@ module CycleAnalyticsHelpers
commit_sha commit_sha
end end
return if skip_push_handler
GitPushService.new(project, GitPushService.new(project,
user, user,
oldrev: oldrev, oldrev: oldrev,
...@@ -44,13 +46,11 @@ module CycleAnalyticsHelpers ...@@ -44,13 +46,11 @@ module CycleAnalyticsHelpers
project.repository.add_branch(user, source_branch, 'master') project.repository.add_branch(user, source_branch, 'master')
end end
sha = project.repository.create_file( # Cycle analytic specs often test with frozen times, which causes metrics to be
user, # pinned to the current time. For example, in the plan stage, we assume that an issue
generate(:branch), # milestone has been created before any code has been written. We add a second
'content', # to ensure that the plan time is positive.
message: commit_message, create_commit(commit_message, project, user, source_branch, commit_time: Time.now + 1.second, skip_push_handler: true)
branch_name: source_branch)
project.repository.commit(sha)
opts = { opts = {
title: 'Awesome merge_request', title: 'Awesome merge_request',
...@@ -116,9 +116,9 @@ module CycleAnalyticsHelpers ...@@ -116,9 +116,9 @@ module CycleAnalyticsHelpers
protected: false) protected: false)
end end
def mock_gitaly_multi_action_dates(raw_repository) def mock_gitaly_multi_action_dates(raw_repository, commit_time)
allow(raw_repository).to receive(:multi_action).and_wrap_original do |m, *args| allow(raw_repository).to receive(:multi_action).and_wrap_original do |m, *args|
new_date = Time.now new_date = commit_time || Time.now
branch_update = m.call(*args) branch_update = m.call(*args)
if branch_update.newrev if branch_update.newrev
......
shared_examples 'file finder' do
let(:query) { 'files' }
let(:search_results) { subject.find(query) }
it 'finds by name' do
filename, blob = search_results.find { |_, blob| blob.filename == expected_file_by_name }
expect(filename).to eq(expected_file_by_name)
expect(blob).to be_a(Gitlab::SearchResults::FoundBlob)
expect(blob.ref).to eq(subject.ref)
expect(blob.data).not_to be_empty
end
it 'finds by content' do
filename, blob = search_results.find { |_, blob| blob.filename == expected_file_by_content }
expect(filename).to eq(expected_file_by_content)
expect(blob).to be_a(Gitlab::SearchResults::FoundBlob)
expect(blob.ref).to eq(subject.ref)
expect(blob.data).not_to be_empty
end
end
require 'spec_helper' require 'spec_helper'
shared_examples_for 'AtomicInternalId' do shared_examples_for 'AtomicInternalId' do |validate_presence: true|
describe '.has_internal_id' do describe '.has_internal_id' do
describe 'Module inclusion' do describe 'Module inclusion' do
subject { described_class } subject { described_class }
...@@ -9,14 +9,31 @@ shared_examples_for 'AtomicInternalId' do ...@@ -9,14 +9,31 @@ shared_examples_for 'AtomicInternalId' do
end end
describe 'Validation' do describe 'Validation' do
subject { instance } before do
allow_any_instance_of(described_class).to receive(:"ensure_#{scope}_#{internal_id_attribute}!")
instance.valid?
end
context 'when presence validation is required' do
before do before do
allow(InternalId).to receive(:generate_next).and_return(nil) skip unless validate_presence
end
it 'validates presence' do
expect(instance.errors[internal_id_attribute]).to include("can't be blank")
end
end end
it { is_expected.to validate_presence_of(internal_id_attribute) } context 'when presence validation is not required' do
it { is_expected.to validate_numericality_of(internal_id_attribute) } before do
skip if validate_presence
end
it 'does not validate presence' do
expect(instance.errors[internal_id_attribute]).to be_empty
end
end
end end
describe 'Creating an instance' do describe 'Creating an instance' do
......
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