Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-01-14 15:07:55 +00:00
parent 4ce0bee95d
commit 85e494935a
63 changed files with 676 additions and 201 deletions

View File

@ -1,3 +1,38 @@
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
.if-default: &if-default
if: '$CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG'
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
.if-default-ee: &if-default-ee
if: '($CI_COMMIT_REF_NAME == "master" || $CI_COMMIT_REF_NAME =~ /^[\d-]+-stable(-ee)?$/ || $CI_COMMIT_REF_NAME =~ /^\d+-\d+-auto-deploy-\d+$/ || $CI_COMMIT_REF_NAME =~ /^security\// || $CI_MERGE_REQUEST_IID || $CI_COMMIT_TAG) && $CI_PROJECT_NAME =~ /^gitlab(-ee)?$/'
# Make sure to update all the similar conditions in other CI config files if you modify these conditions
.if-master: &if-master
if: '$CI_COMMIT_REF_NAME == "master"'
# Make sure to update all the similar patterns in other CI config files if you modify these patterns
.code-backstage-patterns: &code-backstage-patterns
- ".gitlab/ci/**/*"
- ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}"
- ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml"
- ".csscomb.json"
- "Dockerfile.assets"
- "*_VERSION"
- "Gemfile{,.lock}"
- "Rakefile"
- "{babel.config,jest.config}.js"
- "config.ru"
- "{package.json,yarn.lock}"
- "{,ee/}{app,bin,config,db,haml_lint,lib,locale,public,scripts,symbol,vendor}/**/*"
- "doc/api/graphql/reference/*" # Files in this folder are auto-generated
# Backstage changes
- "Dangerfile"
- "danger/**/*"
- "{,ee/}fixtures/**/*"
- "{,ee/}rubocop/**/*"
- "{,ee/}spec/**/*"
- "doc/README.md" # Some RSpec test rely on this file
.assets-compile-cache:
cache:
paths:
@ -132,7 +167,6 @@ compile-assets pull-cache foss:
- .use-pg9
stage: test
needs: ["setup-test-env", "compile-assets pull-cache"]
dependencies: ["setup-test-env", "compile-assets pull-cache"]
.karma-base:
extends: .only-code-frontend-job-base
@ -204,9 +238,10 @@ jest-foss:
- .default-tags
- .default-retry
- .default-cache
- .default-only
- .only:changes-code-backstage
stage: test
rules:
- <<: *if-master
when: on_success
dependencies: []
cache:
key: "$CI_JOB_NAME"
@ -237,11 +272,12 @@ webpack-dev-server:
- .default-tags
- .default-retry
- .default-cache
- .default-only
- .only:changes-code-backstage
stage: test
rules:
- <<: *if-default
changes: *code-backstage-patterns
when: on_success
needs: ["setup-test-env", "compile-assets pull-cache"]
dependencies: ["setup-test-env", "compile-assets pull-cache"]
variables:
WEBPACK_MEMORY_TEST: "true"
WEBPACK_VENDOR_DLL: "true"

View File

@ -5,7 +5,7 @@ Please describe the proposal and add a link to the source (for example, http://w
-->
- [ ] Mention the proposal in the next backend weekly call and the #backend channel to encourage contribution
- [ ] Proceed with the proposal once 50% of the maintainers have weighed in, and 80% of the votes are :+1:
- [ ] Proceed with the proposal once 50% of the maintainers have weighed in, and 80% of their votes are :+1:
- [ ] Once approved, mention it again in the next backend weekly call and the #backend channel

View File

@ -283,7 +283,7 @@ gem 'rack-proxy', '~> 0.6.0'
gem 'sassc-rails', '~> 2.1.0'
gem 'uglifier', '~> 2.7.2'
gem 'addressable', '~> 2.5.2'
gem 'addressable', '~> 2.7'
gem 'font-awesome-rails', '~> 4.7'
gem 'gemojione', '~> 3.3'
gem 'gon', '~> 6.2'
@ -419,7 +419,7 @@ group :test do
gem 'guard-rspec'
end
gem 'octokit', '~> 4.9'
gem 'octokit', '~> 4.15'
gem 'mail_room', '~> 0.10.0'

View File

@ -55,8 +55,8 @@ GEM
adamantium (0.2.0)
ice_nine (~> 0.11.0)
memoizable (~> 0.4.0)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
aes_key_wrap (1.0.1)
akismet (3.0.0)
apollo_upload_server (2.0.0.beta.3)
@ -650,7 +650,8 @@ GEM
multi_json (~> 1.3)
multi_xml (~> 0.5)
rack (>= 1.2, < 3)
octokit (4.9.0)
octokit (4.15.0)
faraday (>= 0.9)
sawyer (~> 0.8.0, >= 0.5.3)
omniauth (1.9.0)
hashie (>= 3.4.6, < 3.7.0)
@ -762,7 +763,7 @@ GEM
pry (~> 0.10)
pry-rails (0.3.6)
pry (>= 0.10.4)
public_suffix (3.1.1)
public_suffix (4.0.3)
pyu-ruby-sasl (0.0.3.3)
raabro (1.1.6)
rack (2.0.7)
@ -961,9 +962,9 @@ GEM
sprockets (> 3.0)
sprockets-rails
tilt
sawyer (0.8.1)
addressable (>= 2.3.5, < 2.6)
faraday (~> 0.8, < 1.0)
sawyer (0.8.2)
addressable (>= 2.3.5)
faraday (> 0.8, < 2.0)
scss_lint (0.56.0)
rake (>= 0.9, < 13)
sass (~> 3.5.3)
@ -1130,7 +1131,7 @@ DEPENDENCIES
acme-client (~> 2.0.2)
activerecord-explain-analyze (~> 0.1)
acts-as-taggable-on (~> 6.0)
addressable (~> 2.5.2)
addressable (~> 2.7)
akismet (~> 3.0)
apollo_upload_server (~> 2.0.0.beta3)
asana (~> 0.9)
@ -1272,7 +1273,7 @@ DEPENDENCIES
net-ssh (~> 5.2)
nokogiri (~> 1.10.5)
oauth2 (~> 1.4)
octokit (~> 4.9)
octokit (~> 4.15)
omniauth (~> 1.8)
omniauth-auth0 (~> 2.0.0)
omniauth-authentiq (~> 0.3.3)

View File

@ -90,7 +90,7 @@ export default {
<template>
<div class="info-well d-none d-sm-flex project-last-commit commit p-3">
<gl-loading-icon v-if="isLoading" size="md" class="m-auto" />
<gl-loading-icon v-if="isLoading" size="md" color="dark" class="m-auto" />
<template v-else>
<user-avatar-link
v-if="commit.author"

View File

@ -44,7 +44,7 @@ export default {
</div>
</div>
<div class="blob-viewer">
<gl-loading-icon v-if="loading > 0" size="md" class="my-4 mx-auto" />
<gl-loading-icon v-if="loading > 0" size="md" color="dark" class="my-4 mx-auto" />
<div v-else-if="readme" v-html="readme.html"></div>
</div>
</article>

View File

@ -34,6 +34,11 @@ export default {
type: Boolean,
required: true,
},
loadingPath: {
type: String,
required: false,
default: '',
},
},
data() {
return {
@ -69,7 +74,12 @@ export default {
<table :aria-label="tableCaption" class="table tree-table qa-file-tree" aria-live="polite">
<table-header v-once />
<tbody>
<parent-row v-show="showParentRow" :commit-ref="ref" :path="path" />
<parent-row
v-show="showParentRow"
:commit-ref="ref"
:path="path"
:loading-path="loadingPath"
/>
<template v-for="val in entries">
<table-row
v-for="entry in val"
@ -84,6 +94,7 @@ export default {
:url="entry.webUrl"
:submodule-tree-url="entry.treeUrl"
:lfs-oid="entry.lfsOid"
:loading-path="loadingPath"
/>
</template>
<template v-if="isLoading">

View File

@ -1,5 +1,10 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
export default {
components: {
GlLoadingIcon,
},
props: {
commitRef: {
type: String,
@ -9,13 +14,21 @@ export default {
type: String,
required: true,
},
loadingPath: {
type: String,
required: false,
default: null,
},
},
computed: {
parentRoute() {
parentPath() {
const splitArray = this.path.split('/');
splitArray.pop();
return { path: `/tree/${this.commitRef}/${splitArray.join('/')}` };
return splitArray.join('/');
},
parentRoute() {
return { path: `/tree/${this.commitRef}/${this.parentPath}` };
},
},
methods: {
@ -29,7 +42,13 @@ export default {
<template>
<tr class="tree-item">
<td colspan="3" class="tree-item-file-name" @click.self="clickRow">
<router-link :to="parentRoute" :aria-label="__('Go to parent')">
<gl-loading-icon
v-if="parentPath === loadingPath"
size="sm"
inline
class="d-inline-block align-text-bottom"
/>
<router-link v-else :to="parentRoute" :aria-label="__('Go to parent')">
..
</router-link>
</td>

View File

@ -1,5 +1,5 @@
<script>
import { GlBadge, GlLink, GlSkeletonLoading, GlTooltipDirective } from '@gitlab/ui';
import { GlBadge, GlLink, GlSkeletonLoading, GlTooltipDirective, GlLoadingIcon } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import TimeagoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import Icon from '~/vue_shared/components/icon.vue';
@ -12,6 +12,7 @@ export default {
GlBadge,
GlLink,
GlSkeletonLoading,
GlLoadingIcon,
TimeagoTooltip,
Icon,
},
@ -76,6 +77,11 @@ export default {
required: false,
default: null,
},
loadingPath: {
type: String,
required: false,
default: '',
},
},
data() {
return {
@ -125,7 +131,13 @@ export default {
<template>
<tr :class="`file_${id}`" class="tree-item" @click="openRow">
<td class="tree-item-file-name">
<i :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i>
<gl-loading-icon
v-if="path === loadingPath"
size="sm"
inline
class="d-inline-block align-text-bottom fa-fw"
/>
<i v-else :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i>
<component :is="linkComponent" :to="routerLinkTo" :href="url" class="str-truncated">
{{ fullPath }}
</component>

View File

@ -27,6 +27,11 @@ export default {
required: false,
default: '/',
},
loadingPath: {
type: String,
required: false,
default: '',
},
},
data() {
return {
@ -109,7 +114,12 @@ export default {
<template>
<div>
<file-table :path="path" :entries="entries" :is-loading="isLoadingFiles" />
<file-table
:path="path"
:entries="entries"
:is-loading="isLoadingFiles"
:loading-path="loadingPath"
/>
<file-preview v-if="readme" :blob="readme" />
</div>
</template>

View File

@ -0,0 +1,36 @@
import getFiles from '../queries/getFiles.query.graphql';
import getRefMixin from './get_ref';
import getProjectPath from '../queries/getProjectPath.query.graphql';
export default {
mixins: [getRefMixin],
apollo: {
projectPath: {
query: getProjectPath,
},
},
data() {
return { projectPath: '', loadingPath: null };
},
beforeRouteUpdate(to, from, next) {
this.preload(to.params.pathMatch, next);
},
methods: {
preload(path, next) {
this.loadingPath = path.replace(/^\//, '');
return this.$apollo
.query({
query: getFiles,
variables: {
projectPath: this.projectPath,
ref: this.ref,
path: this.loadingPath,
nextPageCursor: '',
pageSize: 100,
},
})
.then(() => next());
},
},
};

View File

@ -1,11 +1,13 @@
<script>
import TreeContent from '../components/tree_content.vue';
import { updateElementsVisibility } from '../utils/dom';
import preloadMixin from '../mixins/preload';
export default {
components: {
TreeContent,
},
mixins: [preloadMixin],
props: {
path: {
type: String,
@ -34,5 +36,5 @@ export default {
</script>
<template>
<tree-content :path="path" />
<tree-content :path="path" :loading-path="loadingPath" />
</template>

View File

@ -39,6 +39,10 @@ class Projects::WikisController < Projects::ApplicationController
if @page
set_encoding_error unless valid_encoding?
# Assign vars expected by MarkupHelper
@ref = params[:version_id]
@path = @page.path
render 'show'
elsif file_blob
send_blob(@project_wiki.repository, file_blob)

View File

@ -132,6 +132,7 @@ module MarkupHelper
pipeline: :wiki,
project: @project,
project_wiki: @project_wiki,
repository: @project_wiki.repository,
page_slug: wiki_page.slug,
issuable_state_filter_enabled: true
)

View File

@ -26,7 +26,7 @@ module Ci
belongs_to :merge_request, class_name: 'MergeRequest'
belongs_to :external_pull_request
has_internal_id :iid, scope: :project, presence: false, ensure_if: -> { !importing? }, init: ->(s) do
has_internal_id :iid, scope: :project, presence: false, track_if: -> { !importing? }, ensure_if: -> { !importing? }, init: ->(s) do
s&.project&.all_pipelines&.maximum(:iid) || s&.project&.all_pipelines&.count
end

View File

@ -27,13 +27,13 @@ module AtomicInternalId
extend ActiveSupport::Concern
class_methods do
def has_internal_id(column, scope:, init:, ensure_if: nil, presence: true) # rubocop:disable Naming/PredicateName
def has_internal_id(column, scope:, init:, ensure_if: nil, track_if: nil, presence: true) # rubocop:disable Naming/PredicateName
# We require init here to retain the ability to recalculate in the absence of a
# InternaLId record (we may delete records in `internal_ids` for example).
# InternalId record (we may delete records in `internal_ids` for example).
raise "has_internal_id requires a init block, none given." unless init
raise "has_internal_id needs to be defined on association." unless self.reflect_on_association(scope)
before_validation :"track_#{scope}_#{column}!", on: :create
before_validation :"track_#{scope}_#{column}!", on: :create, if: track_if
before_validation :"ensure_#{scope}_#{column}!", on: :create, if: ensure_if
validates column, presence: presence

View File

@ -5,6 +5,7 @@ class Deployment < ApplicationRecord
include IidRoutes
include AfterCommitQueue
include UpdatedAtFilterable
include Importable
include Gitlab::Utils::StrongMemoize
belongs_to :project, required: true
@ -17,7 +18,7 @@ class Deployment < ApplicationRecord
has_many :merge_requests,
through: :deployment_merge_requests
has_internal_id :iid, scope: :project, init: ->(s) do
has_internal_id :iid, scope: :project, track_if: -> { !importing? }, init: ->(s) do
Deployment.where(project: s.project).maximum(:iid) if s&.project
end

View File

@ -31,7 +31,7 @@ class Issue < ApplicationRecord
belongs_to :duplicated_to, class_name: 'Issue'
belongs_to :closed_by, class_name: 'User'
has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.issues&.maximum(:iid) }
has_internal_id :iid, scope: :project, track_if: -> { !importing? }, init: ->(s) { s&.project&.issues&.maximum(:iid) }
has_many :issue_milestones
has_many :milestones, through: :issue_milestones
@ -78,8 +78,8 @@ class Issue < ApplicationRecord
ignore_column :state, remove_with: '12.7', remove_after: '2019-12-22'
after_commit :expire_etag_cache
after_save :ensure_metrics, unless: :imported?
after_commit :expire_etag_cache, unless: :importing?
after_save :ensure_metrics, unless: :importing?
attr_spammable :title, spam_title: true
attr_spammable :description, spam_description: true

View File

@ -31,7 +31,7 @@ class MergeRequest < ApplicationRecord
belongs_to :source_project, class_name: "Project"
belongs_to :merge_user, class_name: "User"
has_internal_id :iid, scope: :target_project, init: ->(s) { s&.target_project&.merge_requests&.maximum(:iid) }
has_internal_id :iid, scope: :target_project, track_if: -> { !importing? }, init: ->(s) { s&.target_project&.merge_requests&.maximum(:iid) }
has_many :merge_request_diffs
@ -97,8 +97,8 @@ class MergeRequest < ApplicationRecord
after_create :ensure_merge_request_diff
after_update :clear_memoized_shas
after_update :reload_diff_if_branch_changed
after_save :ensure_metrics
after_commit :expire_etag_cache
after_save :ensure_metrics, unless: :importing?
after_commit :expire_etag_cache, unless: :importing?
# When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests

View File

@ -138,7 +138,7 @@ class MergeRequestDiff < ApplicationRecord
# All diff information is collected from repository after object is created.
# It allows you to override variables like head_commit_sha before getting diff.
after_create :save_git_content, unless: :importing?
after_create_commit :set_as_latest_diff
after_create_commit :set_as_latest_diff, unless: :importing?
after_save :update_external_diff_store, if: -> { !importing? && saved_change_to_external_diff? }

View File

@ -17,6 +17,7 @@ class Milestone < ApplicationRecord
include StripAttribute
include Milestoneish
include FromUnion
include Importable
include Gitlab::SQL::Pattern
prepend_if_ee('::EE::Milestone') # rubocop: disable Cop/InjectEnterpriseEditionModule
@ -30,8 +31,8 @@ class Milestone < ApplicationRecord
has_many :milestone_releases
has_many :releases, through: :milestone_releases
has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.milestones&.maximum(:iid) }
has_internal_id :iid, scope: :group, init: ->(s) { s&.group&.milestones&.maximum(:iid) }
has_internal_id :iid, scope: :project, track_if: -> { !importing? }, init: ->(s) { s&.project&.milestones&.maximum(:iid) }
has_internal_id :iid, scope: :group, track_if: -> { !importing? }, init: ->(s) { s&.group&.milestones&.maximum(:iid) }
has_many :issues
has_many :labels, -> { distinct.reorder('labels.title') }, through: :issues

View File

@ -3,6 +3,7 @@
class Release < ApplicationRecord
include Presentable
include CacheMarkdownField
include Importable
include Gitlab::Utils::StrongMemoize
cache_markdown_field :description
@ -33,8 +34,8 @@ class Release < ApplicationRecord
delegate :repository, to: :project
after_commit :create_evidence!, on: :create
after_commit :notify_new_release, on: :create
after_commit :create_evidence!, on: :create, unless: :importing?
after_commit :notify_new_release, on: :create, unless: :importing?
MAX_NUMBER_TO_DISPLAY = 3

View File

@ -20,6 +20,7 @@ class User < ApplicationRecord
include WithUploads
include OptionallySearch
include FromUnion
include BatchDestroyDependentAssociations
DEFAULT_NOTIFICATION_LEVEL = :participating

View File

@ -56,6 +56,13 @@ module Users
MigrateToGhostUserService.new(user).execute unless options[:hard_delete]
if Feature.enabled?(:destroy_user_associations_in_batches)
# Rails attempts to load all related records into memory before
# destroying: https://github.com/rails/rails/issues/22510
# This ensures we delete records in batches.
user.destroy_dependent_associations_in_batches
end
# Destroy the namespace after destroying the user since certain methods may depend on the namespace existing
user_data = user.destroy
namespace.destroy

View File

@ -0,0 +1,5 @@
---
title: Fix error in Wiki when rendering the AsciiDoc include directive
merge_request: 22565
author:
type: fixed

View File

@ -0,0 +1,6 @@
---
title: 'Fix Issue API: creating with manual IID returns conflict when IID already
in use'
merge_request: 22788
author: Mara Sophie Grosch
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Add comment_on_event_enabled to services API
merge_request:
author:
type: added

View File

@ -0,0 +1,5 @@
---
title: Add `importing?` to disable some callbacks
merge_request:
author:
type: performance

View File

@ -0,0 +1,5 @@
---
title: Upgrade octokit and its dependencies
merge_request: 22946
author:
type: other

View File

@ -0,0 +1,5 @@
---
title: 'Use IS08601.3 format for app level logging of timestamps'
merge_request: 22793
author:
type: other

View File

@ -32,7 +32,8 @@ Example response:
"confidential_note_events": true,
"pipeline_events": true,
"wiki_page_events": true,
"job_events": true
"job_events": true,
"comment_on_event_enabled": true
}
{
"id": 76,
@ -50,7 +51,8 @@ Example response:
"confidential_note_events": true,
"pipeline_events": true,
"wiki_page_events": true,
"job_events": true
"job_events": true,
"comment_on_event_enabled": true
}
]
```
@ -723,6 +725,7 @@ Parameters:
| `jira_issue_transition_id` | string | no | The ID of a transition that moves issues to a closed state. You can find this number under the Jira workflow administration (**Administration > Issues > Workflows**) by selecting **View** under **Operations** of the desired workflow of your project. The ID of each state can be found inside the parenthesis of each transition name under the **Transitions (id)** column. By default, this ID is set to `2`. |
| `commit_events` | boolean | false | Enable notifications for commit events |
| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
| `comment_on_event_enabled` | boolean | false | Enable comments inside Jira issues on each GitLab event (commit / merge request) |
### Delete Jira service
@ -761,6 +764,7 @@ Example response:
"note_events": true,
"job_events": true,
"pipeline_events": true,
"comment_on_event_enabled": false,
"properties": {
"token": "<your_access_token>"
}

View File

@ -191,7 +191,7 @@ Here are some examples of how messages would be handled by both the loggers.
FancyMultiLogger.info("Information")
# UnstructuredLogger
I, [2020-01-13T12:02:41.566219 #6652] INFO -- : Information
I, [2020-01-13T18:48:49.201Z #5647] INFO -- : Information
# StructuredLogger
{:severity=>"INFO", :time=>"2020-01-13T11:02:41.559Z", :correlation_id=>"b1701f7ecc4be4bcd4c2d123b214e65a", :message=>"Information"}
@ -203,7 +203,7 @@ I, [2020-01-13T12:02:41.566219 #6652] INFO -- : Information
FancyMultiLogger.info({:message=>"This is my message", :project_id=>123})
# UnstructuredLogger
I, [2020-01-13T12:06:09.856766 #8049] INFO -- : {:message=>"This is my message", :project_id=>123}
I, [2020-01-13T19:01:17.091Z #11056] INFO -- : {"message"=>"Message", "project_id"=>"123"}
# StructuredLogger
{:severity=>"INFO", :time=>"2020-01-13T11:06:09.851Z", :correlation_id=>"d7e0886f096db9a8526a4f89da0e45f6", :message=>"This is my message", :project_id=>123}

View File

@ -38,14 +38,40 @@ GitLab is developed for Linux-based operating systems.
It does **not** run on Microsoft Windows, and we have no plans to support it in the near future. For the latest development status view this [issue](https://gitlab.com/gitlab-org/gitlab/issues/22337).
Please consider using a virtual machine to run GitLab.
## Ruby versions
## Software requirements
GitLab requires Ruby (MRI) 2.6. Support for Ruby versions below 2.6 (2.4, 2.5) will stop with GitLab 12.2.
### Ruby versions
You will have to use the standard MRI implementation of Ruby.
We love [JRuby](https://www.jruby.org/) and [Rubinius](https://rubinius.com) but GitLab
GitLab requires Ruby (MRI) 2.6. Beginning in GitLab 12.2, we no longer support Ruby 2.5 and lower.
You must use the standard MRI implementation of Ruby.
We love [JRuby](https://www.jruby.org/) and [Rubinius](https://rubinius.com), but GitLab
needs several Gems that have native extensions.
### Go versions
The minimum required Go version is 1.12.
### Git versions
GitLab 11.11 and higher only supports Git 2.21.x and newer, and
[dropped support for older versions](https://gitlab.com/gitlab-org/gitlab-foss/issues/54255).
### Node.js versions
Beginning in GitLab 11.8, we only support Node.js 8.10.0 or higher, and dropped
support for Node.js 6.
We recommend Node 12.x, as it is faster.
GitLab uses [webpack](https://webpack.js.org/) to compile frontend assets, which requires a minimum
version of Node.js 8.10.0.
You can check which version you are running with `node -v`. If you are running
a version older than `v8.10.0`, you need to update to a newer version. You
can find instructions to install from community maintained packages or compile
from source at the [Node.js website](https://nodejs.org/en/download).
## Hardware requirements
### Storage

View File

@ -82,24 +82,15 @@ Install Bundler:
sudo gem install bundler --no-document --version '< 2'
```
### 4. Update Node
### 4. Update Node.js
NOTE: Beginning in GitLab 11.8, we only support node 8 or higher, and dropped
support for node 6. Be sure to upgrade if necessary.
NOTE: To check the minimum required Node.js version, see [Node.js versions](../install/requirements.md#nodejs-versions).
GitLab utilizes [webpack](https://webpack.js.org/) to compile frontend assets.
This requires a minimum version of node v8.10.0.
You can check which version you are running with `node -v`. If you are running
a version older than `v8.10.0` you will need to update to a newer version. You
can find instructions to install from community maintained packages or compile
from source at the nodejs.org website.
<https://nodejs.org/en/download/>
GitLab also requires the use of yarn `>= v1.10.0` to manage JavaScript
GitLab also requires the use of Yarn `>= v1.10.0` to manage JavaScript
dependencies.
In Debian or Ubuntu:
```bash
curl --silent --show-error https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
@ -107,34 +98,33 @@ sudo apt-get update
sudo apt-get install yarn
```
More information can be found on the [yarn website](https://yarnpkg.com/en/docs/install).
More information can be found on the [Yarn website](https://yarnpkg.com/en/docs/install).
### 5. Update Go
NOTE: GitLab 11.4 and higher only supports Go 1.10.x and newer, and dropped support for Go
1.9.x. Be sure to upgrade your installation if necessary.
NOTE: To check the minimum required Go version, see [Go versions](../install/requirements.md#go-versions).
You can check which version you are running with `go version`.
Download and install Go:
Download and install Go (for Linux, 64-bit):
```bash
# Remove former Go installation folder
sudo rm -rf /usr/local/go
curl --remote-name --progress https://dl.google.com/go/go1.11.10.linux-amd64.tar.gz
echo 'aefaa228b68641e266d1f23f1d95dba33f17552ba132878b65bb798ffa37e6d0 go1.11.10.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.11.10.linux-amd64.tar.gz
curl --remote-name --progress https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz
echo '512103d7ad296467814a6e3f635631bd35574cab3369a97a323c9a585ccaa569 go1.13.5.linux-amd64.tar.gz' | shasum -a256 -c - && \
sudo tar -C /usr/local -xzf go1.13.5.linux-amd64.tar.gz
sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
rm go1.11.10.linux-amd64.tar.gz
rm go1.13.5.linux-amd64.tar.gz
```
### 6. Update Git
NOTE: **Note:**
GitLab 11.11 and higher only supports Git 2.21.x and newer, and
[dropped support for older versions](https://gitlab.com/gitlab-org/gitlab-foss/issues/54255).
Be sure to upgrade your installation if necessary.
NOTE: To check the minimum required Git version, see [Git versions](../install/requirements.md#git-versions).
In Debian or Ubuntu:
```bash
# Make sure Git is version 2.21.0 or higher
@ -254,9 +244,8 @@ sudo -u git -H make
#### New configuration options for `gitlab.yml`
There might be configuration options available for [`gitlab.yml`][yaml]. View
them with the command below and apply them manually to your current
`gitlab.yml`:
There might be configuration options available for [`gitlab.yml`](https://gitlab.com/gitlab-org/gitlab/blob/master/config/gitlab.yml.example)).
View them with the command below and apply them manually to your current `gitlab.yml`:
```sh
cd /home/git/gitlab
@ -282,7 +271,7 @@ If you are using Strict-Transport-Security in your installation to continue
using it you must enable it in your NGINX configuration as GitLab application no
longer handles setting it.
If you are using Apache instead of NGINX please see the updated [Apache templates].
If you are using Apache instead of NGINX see the updated [Apache templates](https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache).
Also note that because Apache does not support upstreams behind Unix sockets you
will need to let GitLab Workhorse listen on a TCP port. You can do this
via [`/etc/default/gitlab`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/support/init.d/gitlab.default.example#L38).
@ -296,13 +285,13 @@ add the following line to `config/initializers/smtp_settings.rb`:
ActionMailer::Base.delivery_method = :smtp
```
See [smtp_settings.rb.sample] as an example.
See [smtp_settings.rb.sample](https://gitlab.com/gitlab-org/gitlab/blob/master/config/initializers/smtp_settings.rb.sample#L13) as an example.
#### Init script
There might be new configuration options available for
[`gitlab.default.example`][gl-example]. View them with the command below and
apply them manually to your current `/etc/default/gitlab`:
[`gitlab.default.example`](https://gitlab.com/gitlab-org/gitlab/blob/master/lib/support/init.d/gitlab.default.example).
View them with the command below and apply them manually to your current `/etc/default/gitlab`:
```sh
cd /home/git/gitlab
@ -389,17 +378,19 @@ Example:
Additional instructions here.
-->
## Things went south? Revert to previous version
## Troubleshooting
### 1. Revert the code to the previous version
To revert to a previous version, you'll need to following the upgrading guides
for the previous version. If you upgraded to 11.8 and want to revert back to
11.7, you'll need to follow the guides for upgrading from 11.6 to 11.7. You can
To revert to a previous version, you need to follow the upgrading guides
for the previous version.
For example, if you have upgraded to GitLab 12.6 and want to revert back to
12.5, you need to follow the guides for upgrading from 12.4 to 12.5. You can
use the version dropdown at the top of the page to select the right version.
When reverting, you should _not_ follow the database migration guides, as the
backup is already migrated to the previous version.
When reverting, you should **not** follow the database migration guides, as the
backup has already been migrated to the previous version.
### 2. Restore from the backup
@ -409,9 +400,4 @@ cd /home/git/gitlab
sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
```
If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
[yaml]: https://gitlab.com/gitlab-org/gitlab/blob/master/config/gitlab.yml.example
[gl-example]: https://gitlab.com/gitlab-org/gitlab/blob/master/lib/support/init.d/gitlab.default.example
[smtp_settings.rb.sample]: https://gitlab.com/gitlab-org/gitlab/blob/master/config/initializers/smtp_settings.rb.sample#L13
[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
If you have more than one backup `*.tar` file, add `BACKUP=timestamp_of_backup` to the above.

View File

@ -464,6 +464,11 @@ chart is used to install this application with a
[`values.yaml`](https://gitlab.com/gitlab-org/gitlab/blob/master/vendor/elastic_stack/values.yaml)
file.
NOTE: **Note:**
The chart will deploy 4 Elasticsearch nodes: 2 masters, 1 data and 1 client node,
with resource requests totalling 0.1 CPU and 3GB RAM. Each data node requests 1.5GB of memory,
which makes it incompatible with clusters of `f1-micro` and `g1-small` instance types.
## Install using GitLab CI (alpha)
> [Introduced](https://gitlab.com/gitlab-org/gitlab/merge_requests/20822) in GitLab 12.6.

View File

@ -1132,7 +1132,7 @@ module API
expose :commit_events, :push_events, :issues_events, :confidential_issues_events
expose :merge_requests_events, :tag_push_events, :note_events
expose :confidential_note_events, :pipeline_events, :wiki_page_events
expose :job_events
expose :job_events, :comment_on_event_enabled
end
class ProjectService < ProjectServiceBasic

View File

@ -220,18 +220,22 @@ module API
issue_params = convert_parameters_from_legacy_format(issue_params)
issue = ::Issues::CreateService.new(user_project,
current_user,
issue_params.merge(request: request, api: true)).execute
begin
issue = ::Issues::CreateService.new(user_project,
current_user,
issue_params.merge(request: request, api: true)).execute
if issue.spam?
render_api_error!({ error: 'Spam detected' }, 400)
end
if issue.spam?
render_api_error!({ error: 'Spam detected' }, 400)
end
if issue.valid?
present issue, with: Entities::Issue, current_user: current_user, project: user_project
else
render_validation_error!(issue)
if issue.valid?
present issue, with: Entities::Issue, current_user: current_user, project: user_project
else
render_validation_error!(issue)
end
rescue ::ActiveRecord::RecordNotUnique
render_api_error!('Duplicated issue', 409)
end
end

View File

@ -7,7 +7,7 @@ module Gitlab
end
def format_message(severity, timestamp, progname, msg)
"#{timestamp.to_s(:long)}: #{msg}\n"
"#{timestamp.utc.iso8601(3)}: #{msg}\n"
end
end
end

View File

@ -13,7 +13,7 @@ module Gitlab
super(logger: Gitlab::AppLogger)
@context = context
@repository = context[:project].try(:repository)
@repository = context[:repository] || context[:project].try(:repository)
# Note: Asciidoctor calls #freeze on extensions, so we can't set new
# instance variables after initialization.
@ -111,7 +111,7 @@ module Gitlab
end
def ref
context[:ref] || context[:project].default_branch
context[:ref] || repository&.root_ref
end
def requested_path

View File

@ -189,7 +189,7 @@ module Gitlab
end
def default_api_endpoint
OmniAuth::Strategies::GitHub.default_options[:client_options][:site]
OmniAuth::Strategies::GitHub.default_options[:client_options][:site] || ::Octokit::Default.api_endpoint
end
def verify_ssl

View File

@ -60,6 +60,7 @@ module Gitlab
diff.importing = true
diff.save
diff.save_git_content
diff.set_as_latest_diff
end
end
end

View File

@ -80,7 +80,7 @@ module Gitlab
if host.present? && api_version.present?
"#{host}/api/#{api_version}"
else
github_options[:site]
github_options[:site] || ::Octokit::Default.api_endpoint
end
end

39
lib/sentry/api_urls.rb Normal file
View File

@ -0,0 +1,39 @@
# frozen_string_literal: true
module Sentry
class ApiUrls
def initialize(url_base)
@uri = URI(url_base).freeze
end
def issues_url
with_path(File.join(@uri.path, '/issues/'))
end
def issue_url(issue_id)
with_path("/api/0/issues/#{escape(issue_id)}/")
end
def projects_url
with_path('/api/0/projects/')
end
def issue_latest_event_url(issue_id)
with_path("/api/0/issues/#{escape(issue_id)}/events/latest/")
end
private
def with_path(new_path)
new_uri = @uri.dup
# Sentry API returns 404 if there are extra slashes in the URL
new_uri.path = new_path.squeeze('/')
new_uri
end
def escape(param)
CGI.escape(param.to_s)
end
end
end

View File

@ -19,6 +19,10 @@ module Sentry
private
def api_urls
@api_urls ||= Sentry::ApiUrls.new(@url)
end
def handle_mapping_exceptions(&block)
yield
rescue KeyError => e

View File

@ -4,20 +4,13 @@ module Sentry
class Client
module Event
def issue_latest_event(issue_id:)
latest_event = http_get(issue_latest_event_api_url(issue_id))[:body]
latest_event = http_get(api_urls.issue_latest_event_url(issue_id))[:body]
map_to_event(latest_event)
end
private
def issue_latest_event_api_url(issue_id)
latest_event_url = URI(url)
latest_event_url.path = "/api/0/issues/#{issue_id}/events/latest/"
latest_event_url
end
def map_to_event(event)
stack_trace = parse_stack_trace(event)

View File

@ -35,14 +35,14 @@ module Sentry
end
def update_issue(issue_id:, params:)
http_put(issue_api_url(issue_id), params)[:body]
http_put(api_urls.issue_url(issue_id), params)[:body]
end
private
def get_issues(**keyword_args)
response = http_get(
issues_api_url,
api_urls.issues_url,
query: list_issue_sentry_query(keyword_args)
)
@ -72,21 +72,7 @@ module Sentry
end
def get_issue(issue_id:)
http_get(issue_api_url(issue_id))[:body]
end
def issues_api_url
issues_url = URI("#{url}/issues/")
issues_url.path.squeeze!('/')
issues_url
end
def issue_api_url(issue_id)
issue_url = URI(url)
issue_url.path = "/api/0/issues/#{CGI.escape(issue_id.to_s)}/"
issue_url
http_get(api_urls.issue_url(issue_id))[:body]
end
def parse_gitlab_issue(plugin_issues)

View File

@ -14,14 +14,7 @@ module Sentry
private
def get_projects
http_get(projects_api_url)[:body]
end
def projects_api_url
projects_url = URI(url)
projects_url.path = '/api/0/projects/'
projects_url
http_get(api_urls.projects_url)[:body]
end
def map_to_projects(projects)

View File

@ -41,6 +41,11 @@ describe "User browses files" do
it "shows the `Browse Directory` link" do
click_link("files")
page.within('.repo-breadcrumb') do
expect(page).to have_link('files')
end
click_link("History")
expect(page).to have_link("Browse Directory").and have_no_link("Browse Code")

View File

@ -19,7 +19,17 @@ describe 'Projects > Files > User browses LFS files' do
it 'is possible to see raw content of LFS pointer' do
click_link 'files'
page.within('.repo-breadcrumb') do
expect(page).to have_link('files')
end
click_link 'lfs'
page.within('.repo-breadcrumb') do
expect(page).to have_link('lfs')
end
click_link 'lfs_object.iso'
expect(page).to have_content 'version https://git-lfs.github.com/spec/v1'
@ -38,6 +48,11 @@ describe 'Projects > Files > User browses LFS files' do
it 'shows an LFS object' do
click_link('files')
page.within('.repo-breadcrumb') do
expect(page).to have_link('files')
end
click_link('lfs')
click_link('lfs_object.iso')

View File

@ -0,0 +1,79 @@
# frozen_string_literal: true
require 'spec_helper'
describe 'User views AsciiDoc page with includes', :js do
let_it_be(:user) { create(:user) }
let_it_be(:wiki_content_selector) { '[data-qa-selector=wiki_page_content]' }
let(:project) { create(:project, :public, :wiki_repo) }
let!(:included_wiki_page) { create_wiki_page('included_page', content: 'Content from the included page')}
let!(:wiki_page) { create_wiki_page('home', content: "Content from the main page.\ninclude::included_page.asciidoc[]") }
def create_wiki_page(title, content:)
attrs = {
title: title,
content: content,
format: :asciidoc
}
create(:wiki_page, wiki: project.wiki, attrs: attrs)
end
before do
sign_in(user)
end
context 'when the file being included exists' do
it 'includes the file contents' do
visit(project_wiki_path(project, wiki_page))
page.within(:css, wiki_content_selector) do
expect(page).to have_content('Content from the main page. Content from the included page')
end
end
context 'when there are multiple versions of the wiki pages' do
before do
included_wiki_page.update(message: 'updated included file', content: 'Updated content from the included page')
wiki_page.update(message: 'updated wiki page', content: "Updated content from the main page.\ninclude::included_page.asciidoc[]")
end
let(:latest_version_id) { wiki_page.versions.first.id }
let(:oldest_version_id) { wiki_page.versions.last.id }
context 'viewing the latest version' do
it 'includes the latest content' do
visit(project_wiki_path(project, wiki_page, version_id: latest_version_id))
page.within(:css, wiki_content_selector) do
expect(page).to have_content('Updated content from the main page. Updated content from the included page')
end
end
end
context 'viewing the original version' do
it 'includes the content from the original version' do
visit(project_wiki_path(project, wiki_page, version_id: oldest_version_id))
page.within(:css, wiki_content_selector) do
expect(page).to have_content('Content from the main page. Content from the included page')
end
end
end
end
end
context 'when the file being included does not exist' do
before do
included_wiki_page.delete
end
it 'outputs an error' do
visit(project_wiki_path(project, wiki_page))
page.within(:css, wiki_content_selector) do
expect(page).to have_content('Content from the main page. [ERROR: include::included_page.asciidoc[] - unresolved directive]')
end
end
end
end

View File

@ -16,7 +16,8 @@
"confidential_note_events": { "type": "boolean" },
"pipeline_events": { "type": "boolean" },
"wiki_page_events": { "type": "boolean" },
"job_events": { "type": "boolean" }
"job_events": { "type": "boolean" },
"comment_on_event_enabled": { "type": "boolean" }
},
"additionalProperties": false
}

View File

@ -1,10 +1,11 @@
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
import { GlLoadingIcon } from '@gitlab/ui';
import ParentRow from '~/repository/components/table/parent_row.vue';
let vm;
let $router;
function factory(path) {
function factory(path, loadingPath) {
$router = {
push: jest.fn(),
};
@ -13,6 +14,7 @@ function factory(path) {
propsData: {
commitRef: 'master',
path,
loadingPath,
},
stubs: {
RouterLink: RouterLinkStub,
@ -61,4 +63,10 @@ describe('Repository parent row component', () => {
path: '/tree/master/app',
});
});
it('renders loading icon when loading parent', () => {
factory('app/assets', 'app');
expect(vm.find(GlLoadingIcon).exists()).toBe(true);
});
});

View File

@ -1,5 +1,5 @@
import { shallowMount, RouterLinkStub } from '@vue/test-utils';
import { GlBadge, GlLink } from '@gitlab/ui';
import { GlBadge, GlLink, GlLoadingIcon } from '@gitlab/ui';
import { visitUrl } from '~/lib/utils/url_utility';
import TableRow from '~/repository/components/table/row.vue';
import Icon from '~/vue_shared/components/icon.vue';
@ -198,4 +198,17 @@ describe('Repository table row component', () => {
expect(vm.find(Icon).exists()).toBe(true);
});
});
it('renders loading icon when path is loading', () => {
factory({
id: '1',
sha: '1',
path: 'test',
type: 'tree',
currentPath: '/',
loadingPath: 'test',
});
expect(vm.find(GlLoadingIcon).exists()).toBe(true);
});
});

View File

@ -273,16 +273,19 @@ describe MarkupHelper do
describe '#render_wiki_content' do
let(:wiki) { double('WikiPage', path: "file.#{extension}") }
let(:wiki_repository) { double('Repository') }
let(:context) do
{
pipeline: :wiki, project: project, project_wiki: wiki,
page_slug: 'nested/page', issuable_state_filter_enabled: true
page_slug: 'nested/page', issuable_state_filter_enabled: true,
repository: wiki_repository
}
end
before do
expect(wiki).to receive(:content).and_return('wiki content')
expect(wiki).to receive(:slug).and_return('nested/page')
expect(wiki).to receive(:repository).and_return(wiki_repository)
helper.instance_variable_set(:@project_wiki, wiki)
end

View File

@ -15,4 +15,11 @@ describe Gitlab::AppTextLogger do
it 'logs a string unchanged' do
expect(subject.format_message('INFO', Time.now, nil, string_message)).to include(string_message)
end
it 'logs time in UTC with ISO8601.3 standard' do
Timecop.freeze do
expect(subject.format_message('INFO', Time.now, nil, string_message))
.to include(Time.now.utc.iso8601(3))
end
end
end

View File

@ -518,6 +518,28 @@ module Gitlab
end
end
context 'when repository is passed into the context' do
let(:wiki_repo) { project.wiki.repository }
let(:include_path) { 'wiki_file.adoc' }
before do
project.create_wiki
context.merge!(repository: wiki_repo)
end
context 'when the file exists' do
before do
create_file(include_path, 'Content from wiki', repository: wiki_repo)
end
it { is_expected.to include('<p>Content from wiki</p>') }
end
context 'when the file does not exist' do
it { is_expected.to include("[ERROR: include::#{include_path}[] - unresolved directive]")}
end
end
context 'recursive includes with relative paths' do
let(:input) do
<<~ADOC
@ -562,8 +584,8 @@ module Gitlab
end
end
def create_file(path, content)
project.repository.create_file(project.creator, path, content,
def create_file(path, content, repository: project.repository)
repository.create_file(project.creator, path, content,
message: "Add #{path}", branch_name: 'asciidoc')
end
end

View File

@ -0,0 +1,85 @@
# frozen_string_literal: true
require 'spec_helper'
describe Sentry::ApiUrls do
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/' }
let(:token) { 'test-token' }
let(:issue_id) { '123456' }
let(:issue_id_with_reserved_chars) { '123$%' }
let(:escaped_issue_id) { '123%24%25' }
let(:api_urls) { Sentry::ApiUrls.new(sentry_url) }
# Sentry API returns 404 if there are extra slashes in the URL!
shared_examples 'correct url with extra slashes' do
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects//sentry-org/sentry-project/' }
it_behaves_like 'correct url'
end
shared_examples 'correctly escapes issue ID' do
context 'with param a string with reserved chars' do
let(:issue_id) { issue_id_with_reserved_chars }
it { expect(subject.to_s).to include(escaped_issue_id) }
end
context 'with param a symbol with reserved chars' do
let(:issue_id) { issue_id_with_reserved_chars.to_sym }
it { expect(subject.to_s).to include(escaped_issue_id) }
end
context 'with param an integer' do
let(:issue_id) { 12345678 }
it { expect(subject.to_s).to include(issue_id.to_s) }
end
end
describe '#issues_url' do
subject { api_urls.issues_url }
shared_examples 'correct url' do
it { is_expected.to eq_uri('https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/issues/') }
end
it_behaves_like 'correct url'
it_behaves_like 'correct url with extra slashes'
end
describe '#issue_url' do
subject { api_urls.issue_url(issue_id) }
shared_examples 'correct url' do
it { is_expected.to eq_uri("https://sentrytest.gitlab.com/api/0/issues/#{issue_id}/") }
end
it_behaves_like 'correct url'
it_behaves_like 'correct url with extra slashes'
it_behaves_like 'correctly escapes issue ID'
end
describe '#projects_url' do
subject { api_urls.projects_url }
shared_examples 'correct url' do
it { is_expected.to eq_uri('https://sentrytest.gitlab.com/api/0/projects/') }
end
it_behaves_like 'correct url'
it_behaves_like 'correct url with extra slashes'
end
describe '#issue_latest_event_url' do
subject { api_urls.issue_latest_event_url(issue_id) }
shared_examples 'correct url' do
it { is_expected.to eq_uri("https://sentrytest.gitlab.com/api/0/issues/#{issue_id}/events/latest/") }
end
it_behaves_like 'correct url'
it_behaves_like 'correct url with extra slashes'
it_behaves_like 'correctly escapes issue ID'
end
end

View File

@ -109,28 +109,6 @@ describe Sentry::Client::Issue do
it_behaves_like 'no Sentry redirects'
end
# Sentry API returns 404 if there are extra slashes in the URL!
context 'extra slashes in URL' do
let(:sentry_url) { 'https://sentrytest.gitlab.com/api/0/projects//sentry-org/sentry-project/' }
let(:sentry_request_url) do
'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/' \
'issues/?limit=20&query=is:unresolved'
end
it 'removes extra slashes in api url' do
expect(client.url).to eq(sentry_url)
expect(Gitlab::HTTP).to receive(:get).with(
URI('https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/issues/'),
anything
).and_call_original
subject
expect(sentry_api_request).to have_been_requested
end
end
context 'requests with sort parameter in sentry api' do
let(:sentry_request_url) do
'https://sentrytest.gitlab.com/api/0/projects/sentry-org/sentry-project/' \
@ -232,14 +210,6 @@ describe Sentry::Client::Issue do
subject { client.issue_details(issue_id: issue_id) }
it 'escapes issue ID' do
allow(CGI).to receive(:escape).and_call_original
subject
expect(CGI).to have_received(:escape).with(issue_id.to_s)
end
context 'error object created from sentry response' do
using RSpec::Parameterized::TableSyntax

View File

@ -91,25 +91,6 @@ describe Sentry::Client::Projects do
it_behaves_like 'no Sentry redirects'
end
# Sentry API returns 404 if there are extra slashes in the URL!
context 'extra slashes in URL' do
let(:sentry_url) { 'https://sentrytest.gitlab.com/api//0/projects//' }
let!(:valid_req_stub) do
stub_sentry_request(sentry_list_projects_url)
end
it 'removes extra slashes in api url' do
expect(Gitlab::HTTP).to receive(:get).with(
URI(sentry_list_projects_url),
anything
).and_call_original
subject
expect(valid_req_stub).to have_been_requested
end
end
context 'when exception is raised' do
let(:sentry_request_url) { sentry_list_projects_url }

View File

@ -9,6 +9,32 @@ describe AtomicInternalId do
let(:scope_attrs) { { project: milestone.project } }
let(:usage) { :milestones }
describe '#save!' do
context 'when IID is provided' do
before do
milestone.iid = external_iid
end
it 'tracks the value' do
expect(milestone).to receive(:track_project_iid!)
milestone.save!
end
context 'when importing' do
before do
milestone.importing = true
end
it 'does not track the value' do
expect(milestone).not_to receive(:track_project_iid!)
milestone.save!
end
end
end
end
describe '#track_project_iid!' do
subject { milestone.track_project_iid! }

View File

@ -160,6 +160,16 @@ describe API::Issues do
expect(json_response['iid']).not_to eq 9001
end
end
context 'when an issue with the same IID exists on database' do
it 'returns 409' do
post api("/projects/#{project.id}/issues", admin),
params: { title: 'new issue', iid: issue.iid }
expect(response).to have_gitlab_http_status(409)
expect(json_response['message']).to eq 'Duplicated issue'
end
end
end
it 'creates a new project issue' do

View File

@ -20,6 +20,22 @@ describe Users::DestroyService do
expect { Namespace.find(namespace.id) }.to raise_error(ActiveRecord::RecordNotFound)
end
it 'deletes user associations in batches' do
expect(user).to receive(:destroy_dependent_associations_in_batches)
service.execute(user)
end
context 'when :destroy_user_associations_in_batches flag is disabled' do
it 'does not delete user associations in batches' do
stub_feature_flags(destroy_user_associations_in_batches: false)
expect(user).not_to receive(:destroy_dependent_associations_in_batches)
service.execute(user)
end
end
it 'will delete the project' do
expect_next_instance_of(Projects::DestroyService) do |destroy_service|
expect(destroy_service).to receive(:execute).once.and_return(true)

View File

@ -0,0 +1,19 @@
# frozen_string_literal: true
# Assert the result matches a URI object initialized with the expectation variable.
#
# Success:
# ```
# expect(URI('www.fish.com')).to eq_uri('www.fish.com')
# ```
#
# Failure:
# ```
# expect(URI('www.fish.com')).to eq_uri('www.dog.com')
# ```
#
RSpec::Matchers.define :eq_uri do |expected|
match do |actual|
actual == URI(expected)
end
end