summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab/ci/frontend.gitlab-ci.yml48
-rw-r--r--.gitlab/issue_templates/Coding style proposal.md2
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock19
-rw-r--r--app/assets/javascripts/repository/components/last_commit.vue2
-rw-r--r--app/assets/javascripts/repository/components/preview/index.vue2
-rw-r--r--app/assets/javascripts/repository/components/table/index.vue13
-rw-r--r--app/assets/javascripts/repository/components/table/parent_row.vue25
-rw-r--r--app/assets/javascripts/repository/components/table/row.vue16
-rw-r--r--app/assets/javascripts/repository/components/tree_content.vue12
-rw-r--r--app/assets/javascripts/repository/mixins/preload.js36
-rw-r--r--app/assets/javascripts/repository/pages/tree.vue4
-rw-r--r--app/controllers/projects/wikis_controller.rb4
-rw-r--r--app/helpers/markup_helper.rb1
-rw-r--r--app/models/ci/pipeline.rb2
-rw-r--r--app/models/concerns/atomic_internal_id.rb6
-rw-r--r--app/models/deployment.rb3
-rw-r--r--app/models/issue.rb6
-rw-r--r--app/models/merge_request.rb6
-rw-r--r--app/models/merge_request_diff.rb2
-rw-r--r--app/models/milestone.rb5
-rw-r--r--app/models/release.rb5
-rw-r--r--app/models/user.rb1
-rw-r--r--app/services/users/destroy_service.rb7
-rw-r--r--changelogs/unreleased/195625-wiki-support-for-asciidoc-include-directive.yml5
-rw-r--r--changelogs/unreleased/196172-issue-create-iid-conflict.yml6
-rw-r--r--changelogs/unreleased/add_comment_on_event_enabled_to_services_api.yml5
-rw-r--r--changelogs/unreleased/mark-some-as-not-required-during-import.yml5
-rw-r--r--changelogs/unreleased/sh-update-octokit.yml5
-rw-r--r--changelogs/unreleased/standardize-timestamp-format-in-app-logs.yml5
-rw-r--r--doc/api/services.md8
-rw-r--r--doc/development/logging.md4
-rw-r--r--doc/install/requirements.md34
-rw-r--r--doc/update/upgrading_from_source.md76
-rw-r--r--doc/user/clusters/applications.md5
-rw-r--r--lib/api/entities.rb2
-rw-r--r--lib/api/issues.rb28
-rw-r--r--lib/gitlab/app_text_logger.rb2
-rw-r--r--lib/gitlab/asciidoc/include_processor.rb4
-rw-r--r--lib/gitlab/github_import/client.rb2
-rw-r--r--lib/gitlab/import/merge_request_helpers.rb1
-rw-r--r--lib/gitlab/legacy_github_import/client.rb2
-rw-r--r--lib/sentry/api_urls.rb39
-rw-r--r--lib/sentry/client.rb4
-rw-r--r--lib/sentry/client/event.rb9
-rw-r--r--lib/sentry/client/issue.rb20
-rw-r--r--lib/sentry/client/projects.rb9
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb5
-rw-r--r--spec/features/projects/files/user_browses_lfs_files_spec.rb15
-rw-r--r--spec/features/projects/wiki/users_views_asciidoc_page_with_includes_spec.rb79
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/service.json3
-rw-r--r--spec/frontend/repository/components/table/parent_row_spec.js10
-rw-r--r--spec/frontend/repository/components/table/row_spec.js15
-rw-r--r--spec/helpers/markup_helper_spec.rb5
-rw-r--r--spec/lib/gitlab/app_text_logger_spec.rb7
-rw-r--r--spec/lib/gitlab/asciidoc_spec.rb26
-rw-r--r--spec/lib/sentry/api_urls_spec.rb85
-rw-r--r--spec/lib/sentry/client/issue_spec.rb30
-rw-r--r--spec/lib/sentry/client/projects_spec.rb19
-rw-r--r--spec/models/concerns/atomic_internal_id_spec.rb26
-rw-r--r--spec/requests/api/issues/post_projects_issues_spec.rb10
-rw-r--r--spec/services/users/destroy_service_spec.rb16
-rw-r--r--spec/support/matchers/eq_uri.rb19
63 files changed, 678 insertions, 203 deletions
diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml
index 8df530c546d..076de55014e 100644
--- a/.gitlab/ci/frontend.gitlab-ci.yml
+++ b/.gitlab/ci/frontend.gitlab-ci.yml
@@ -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"
diff --git a/.gitlab/issue_templates/Coding style proposal.md b/.gitlab/issue_templates/Coding style proposal.md
index a969c9b72ee..95f0fb5f366 100644
--- a/.gitlab/issue_templates/Coding style proposal.md
+++ b/.gitlab/issue_templates/Coding style proposal.md
@@ -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
diff --git a/Gemfile b/Gemfile
index 8c493896f08..83cfb7bbf14 100644
--- a/Gemfile
+++ b/Gemfile
@@ -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'
diff --git a/Gemfile.lock b/Gemfile.lock
index 13e1e4ac828..f55fb554c4a 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -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)
diff --git a/app/assets/javascripts/repository/components/last_commit.vue b/app/assets/javascripts/repository/components/last_commit.vue
index 70678b0db37..8e7529899c0 100644
--- a/app/assets/javascripts/repository/components/last_commit.vue
+++ b/app/assets/javascripts/repository/components/last_commit.vue
@@ -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"
diff --git a/app/assets/javascripts/repository/components/preview/index.vue b/app/assets/javascripts/repository/components/preview/index.vue
index 6b3822151ff..2bc93c3f1c1 100644
--- a/app/assets/javascripts/repository/components/preview/index.vue
+++ b/app/assets/javascripts/repository/components/preview/index.vue
@@ -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>
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue
index 8f2e9264bca..29a3340b83d 100644
--- a/app/assets/javascripts/repository/components/table/index.vue
+++ b/app/assets/javascripts/repository/components/table/index.vue
@@ -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">
diff --git a/app/assets/javascripts/repository/components/table/parent_row.vue b/app/assets/javascripts/repository/components/table/parent_row.vue
index 3c39f404226..70a188f98cc 100644
--- a/app/assets/javascripts/repository/components/table/parent_row.vue
+++ b/app/assets/javascripts/repository/components/table/parent_row.vue
@@ -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>
diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue
index cf0457a2abf..a8e13241c37 100644
--- a/app/assets/javascripts/repository/components/table/row.vue
+++ b/app/assets/javascripts/repository/components/table/row.vue
@@ -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>
diff --git a/app/assets/javascripts/repository/components/tree_content.vue b/app/assets/javascripts/repository/components/tree_content.vue
index 949e653fc8f..c30d6f05c6a 100644
--- a/app/assets/javascripts/repository/components/tree_content.vue
+++ b/app/assets/javascripts/repository/components/tree_content.vue
@@ -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>
diff --git a/app/assets/javascripts/repository/mixins/preload.js b/app/assets/javascripts/repository/mixins/preload.js
new file mode 100644
index 00000000000..e68996245a8
--- /dev/null
+++ b/app/assets/javascripts/repository/mixins/preload.js
@@ -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());
+ },
+ },
+};
diff --git a/app/assets/javascripts/repository/pages/tree.vue b/app/assets/javascripts/repository/pages/tree.vue
index dd4d437f4dd..adc332fa370 100644
--- a/app/assets/javascripts/repository/pages/tree.vue
+++ b/app/assets/javascripts/repository/pages/tree.vue
@@ -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>
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index fb06299676c..cfc0925d9e1 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -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)
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index e1e756c2f4c..657e5accdab 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -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
)
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 5ad3a9b4431..663389050d1 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -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
diff --git a/app/models/concerns/atomic_internal_id.rb b/app/models/concerns/atomic_internal_id.rb
index 64df265dc25..3e9b084e784 100644
--- a/app/models/concerns/atomic_internal_id.rb
+++ b/app/models/concerns/atomic_internal_id.rb
@@ -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
diff --git a/app/models/deployment.rb b/app/models/deployment.rb
index 5a22a6ada9d..74cc7f93580 100644
--- a/app/models/deployment.rb
+++ b/app/models/deployment.rb
@@ -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
diff --git a/app/models/issue.rb b/app/models/issue.rb
index da6450c6092..bf600278162 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -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
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 61f10620e39..6be14abe744 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -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
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 71a344e69e3..fa633a1a725 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -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? }
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 920c28aeceb..5da92fc4bc5 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -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
diff --git a/app/models/release.rb b/app/models/release.rb
index 823fd61eebd..ecfae554fe0 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -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
diff --git a/app/models/user.rb b/app/models/user.rb
index b9efbcdf502..6442e74bbe3 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -20,6 +20,7 @@ class User < ApplicationRecord
include WithUploads
include OptionallySearch
include FromUnion
+ include BatchDestroyDependentAssociations
DEFAULT_NOTIFICATION_LEVEL = :participating
diff --git a/app/services/users/destroy_service.rb b/app/services/users/destroy_service.rb
index e341c7f0537..643ebdc6839 100644
--- a/app/services/users/destroy_service.rb
+++ b/app/services/users/destroy_service.rb
@@ -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
diff --git a/changelogs/unreleased/195625-wiki-support-for-asciidoc-include-directive.yml b/changelogs/unreleased/195625-wiki-support-for-asciidoc-include-directive.yml
new file mode 100644
index 00000000000..2e9c65f35af
--- /dev/null
+++ b/changelogs/unreleased/195625-wiki-support-for-asciidoc-include-directive.yml
@@ -0,0 +1,5 @@
+---
+title: Fix error in Wiki when rendering the AsciiDoc include directive
+merge_request: 22565
+author:
+type: fixed
diff --git a/changelogs/unreleased/196172-issue-create-iid-conflict.yml b/changelogs/unreleased/196172-issue-create-iid-conflict.yml
new file mode 100644
index 00000000000..bb0ea686f91
--- /dev/null
+++ b/changelogs/unreleased/196172-issue-create-iid-conflict.yml
@@ -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
diff --git a/changelogs/unreleased/add_comment_on_event_enabled_to_services_api.yml b/changelogs/unreleased/add_comment_on_event_enabled_to_services_api.yml
new file mode 100644
index 00000000000..77acad7af37
--- /dev/null
+++ b/changelogs/unreleased/add_comment_on_event_enabled_to_services_api.yml
@@ -0,0 +1,5 @@
+---
+title: Add comment_on_event_enabled to services API
+merge_request:
+author:
+type: added
diff --git a/changelogs/unreleased/mark-some-as-not-required-during-import.yml b/changelogs/unreleased/mark-some-as-not-required-during-import.yml
new file mode 100644
index 00000000000..1d8449728ba
--- /dev/null
+++ b/changelogs/unreleased/mark-some-as-not-required-during-import.yml
@@ -0,0 +1,5 @@
+---
+title: Add `importing?` to disable some callbacks
+merge_request:
+author:
+type: performance
diff --git a/changelogs/unreleased/sh-update-octokit.yml b/changelogs/unreleased/sh-update-octokit.yml
new file mode 100644
index 00000000000..d57a03e03c7
--- /dev/null
+++ b/changelogs/unreleased/sh-update-octokit.yml
@@ -0,0 +1,5 @@
+---
+title: Upgrade octokit and its dependencies
+merge_request: 22946
+author:
+type: other
diff --git a/changelogs/unreleased/standardize-timestamp-format-in-app-logs.yml b/changelogs/unreleased/standardize-timestamp-format-in-app-logs.yml
new file mode 100644
index 00000000000..c4966c2e77f
--- /dev/null
+++ b/changelogs/unreleased/standardize-timestamp-format-in-app-logs.yml
@@ -0,0 +1,5 @@
+---
+title: 'Use IS08601.3 format for app level logging of timestamps'
+merge_request: 22793
+author:
+type: other
diff --git a/doc/api/services.md b/doc/api/services.md
index 2b41b532f1d..f11cf7fd1b1 100644
--- a/doc/api/services.md
+++ b/doc/api/services.md
@@ -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>"
}
diff --git a/doc/development/logging.md b/doc/development/logging.md
index 5d6b2d535b8..ea099de21d5 100644
--- a/doc/development/logging.md
+++ b/doc/development/logging.md
@@ -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}
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index 7c5730f9b07..9bc1658d59c 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -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
diff --git a/doc/update/upgrading_from_source.md b/doc/update/upgrading_from_source.md
index aeb99261c4b..48f8052cbb8 100644
--- a/doc/update/upgrading_from_source.md
+++ b/doc/update/upgrading_from_source.md
@@ -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.
diff --git a/doc/user/clusters/applications.md b/doc/user/clusters/applications.md
index f2be9b2f3ac..f498645466f 100644
--- a/doc/user/clusters/applications.md
+++ b/doc/user/clusters/applications.md
@@ -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.
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index e8ea4a9fac5..74f8edb0784 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -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
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index d0772c70ffb..4e21815fa35 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -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
-
- 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)
+ 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.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
diff --git a/lib/gitlab/app_text_logger.rb b/lib/gitlab/app_text_logger.rb
index 59ac57b2bb4..5b0439f43ad 100644
--- a/lib/gitlab/app_text_logger.rb
+++ b/lib/gitlab/app_text_logger.rb
@@ -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
diff --git a/lib/gitlab/asciidoc/include_processor.rb b/lib/gitlab/asciidoc/include_processor.rb
index c6fbf540e9c..6e0b7ce60ba 100644
--- a/lib/gitlab/asciidoc/include_processor.rb
+++ b/lib/gitlab/asciidoc/include_processor.rb
@@ -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
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index 826b35d685c..22803c5cd71 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -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
diff --git a/lib/gitlab/import/merge_request_helpers.rb b/lib/gitlab/import/merge_request_helpers.rb
index 4bc39868389..c5694d95aa1 100644
--- a/lib/gitlab/import/merge_request_helpers.rb
+++ b/lib/gitlab/import/merge_request_helpers.rb
@@ -60,6 +60,7 @@ module Gitlab
diff.importing = true
diff.save
diff.save_git_content
+ diff.set_as_latest_diff
end
end
end
diff --git a/lib/gitlab/legacy_github_import/client.rb b/lib/gitlab/legacy_github_import/client.rb
index b23efd64dee..34634d20a16 100644
--- a/lib/gitlab/legacy_github_import/client.rb
+++ b/lib/gitlab/legacy_github_import/client.rb
@@ -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
diff --git a/lib/sentry/api_urls.rb b/lib/sentry/api_urls.rb
new file mode 100644
index 00000000000..388d0531da1
--- /dev/null
+++ b/lib/sentry/api_urls.rb
@@ -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
diff --git a/lib/sentry/client.rb b/lib/sentry/client.rb
index 29b00a6f8b2..20c919a97ff 100644
--- a/lib/sentry/client.rb
+++ b/lib/sentry/client.rb
@@ -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
diff --git a/lib/sentry/client/event.rb b/lib/sentry/client/event.rb
index 8bcada5a9ce..01dfaa25969 100644
--- a/lib/sentry/client/event.rb
+++ b/lib/sentry/client/event.rb
@@ -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)
diff --git a/lib/sentry/client/issue.rb b/lib/sentry/client/issue.rb
index b3b3776624b..1c5d88e8862 100644
--- a/lib/sentry/client/issue.rb
+++ b/lib/sentry/client/issue.rb
@@ -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)
diff --git a/lib/sentry/client/projects.rb b/lib/sentry/client/projects.rb
index 68f8fe0f9c9..e686d4ff715 100644
--- a/lib/sentry/client/projects.rb
+++ b/lib/sentry/client/projects.rb
@@ -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)
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index 657513431e5..b8efabb0cab 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -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")
diff --git a/spec/features/projects/files/user_browses_lfs_files_spec.rb b/spec/features/projects/files/user_browses_lfs_files_spec.rb
index 618290416bd..dbeec973865 100644
--- a/spec/features/projects/files/user_browses_lfs_files_spec.rb
+++ b/spec/features/projects/files/user_browses_lfs_files_spec.rb
@@ -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')
diff --git a/spec/features/projects/wiki/users_views_asciidoc_page_with_includes_spec.rb b/spec/features/projects/wiki/users_views_asciidoc_page_with_includes_spec.rb
new file mode 100644
index 00000000000..08eea14c438
--- /dev/null
+++ b/spec/features/projects/wiki/users_views_asciidoc_page_with_includes_spec.rb
@@ -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
diff --git a/spec/fixtures/api/schemas/public_api/v4/service.json b/spec/fixtures/api/schemas/public_api/v4/service.json
index 4a91d264961..a024e38acad 100644
--- a/spec/fixtures/api/schemas/public_api/v4/service.json
+++ b/spec/fixtures/api/schemas/public_api/v4/service.json
@@ -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
}
diff --git a/spec/frontend/repository/components/table/parent_row_spec.js b/spec/frontend/repository/components/table/parent_row_spec.js
index 7020055271f..439c7ff080c 100644
--- a/spec/frontend/repository/components/table/parent_row_spec.js
+++ b/spec/frontend/repository/components/table/parent_row_spec.js
@@ -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);
+ });
});
diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js
index 067bad0dab6..b60560366a6 100644
--- a/spec/frontend/repository/components/table/row_spec.js
+++ b/spec/frontend/repository/components/table/row_spec.js
@@ -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);
+ });
});
diff --git a/spec/helpers/markup_helper_spec.rb b/spec/helpers/markup_helper_spec.rb
index 66d461a871c..f415fb05b5b 100644
--- a/spec/helpers/markup_helper_spec.rb
+++ b/spec/helpers/markup_helper_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/app_text_logger_spec.rb b/spec/lib/gitlab/app_text_logger_spec.rb
index 06d3e643608..c84b986ce40 100644
--- a/spec/lib/gitlab/app_text_logger_spec.rb
+++ b/spec/lib/gitlab/app_text_logger_spec.rb
@@ -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
diff --git a/spec/lib/gitlab/asciidoc_spec.rb b/spec/lib/gitlab/asciidoc_spec.rb
index ba5b70b44de..c8d159d1e84 100644
--- a/spec/lib/gitlab/asciidoc_spec.rb
+++ b/spec/lib/gitlab/asciidoc_spec.rb
@@ -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
diff --git a/spec/lib/sentry/api_urls_spec.rb b/spec/lib/sentry/api_urls_spec.rb
new file mode 100644
index 00000000000..78455f8d51f
--- /dev/null
+++ b/spec/lib/sentry/api_urls_spec.rb
@@ -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
diff --git a/spec/lib/sentry/client/issue_spec.rb b/spec/lib/sentry/client/issue_spec.rb
index e10d27e4342..3130bb38cc2 100644
--- a/spec/lib/sentry/client/issue_spec.rb
+++ b/spec/lib/sentry/client/issue_spec.rb
@@ -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
diff --git a/spec/lib/sentry/client/projects_spec.rb b/spec/lib/sentry/client/projects_spec.rb
index 462f74eaac9..6183d4c5816 100644
--- a/spec/lib/sentry/client/projects_spec.rb
+++ b/spec/lib/sentry/client/projects_spec.rb
@@ -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 }
diff --git a/spec/models/concerns/atomic_internal_id_spec.rb b/spec/models/concerns/atomic_internal_id_spec.rb
index 0605392c0aa..93bf7ec10dd 100644
--- a/spec/models/concerns/atomic_internal_id_spec.rb
+++ b/spec/models/concerns/atomic_internal_id_spec.rb
@@ -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! }
diff --git a/spec/requests/api/issues/post_projects_issues_spec.rb b/spec/requests/api/issues/post_projects_issues_spec.rb
index e9f678d164e..67404cf10df 100644
--- a/spec/requests/api/issues/post_projects_issues_spec.rb
+++ b/spec/requests/api/issues/post_projects_issues_spec.rb
@@ -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
diff --git a/spec/services/users/destroy_service_spec.rb b/spec/services/users/destroy_service_spec.rb
index 23a0c71175e..d9335cef5cc 100644
--- a/spec/services/users/destroy_service_spec.rb
+++ b/spec/services/users/destroy_service_spec.rb
@@ -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)
diff --git a/spec/support/matchers/eq_uri.rb b/spec/support/matchers/eq_uri.rb
new file mode 100644
index 00000000000..47b657b3fe1
--- /dev/null
+++ b/spec/support/matchers/eq_uri.rb
@@ -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