summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml128
-rw-r--r--GITALY_SERVER_VERSION2
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/javascripts/behaviors/copy_as_gfm.js7
-rw-r--r--app/assets/javascripts/deploy_keys/components/app.vue1
-rw-r--r--app/assets/javascripts/dispatcher.js25
-rw-r--r--app/assets/javascripts/groups/index.js4
-rw-r--r--app/assets/javascripts/groups/service/groups_service.js4
-rw-r--r--app/assets/javascripts/pages/dashboard/groups/index/index.js5
-rw-r--r--app/assets/javascripts/pages/explore/groups/index.js2
-rw-r--r--app/assets/javascripts/pages/groups/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/snippets/show/index.js4
-rw-r--r--app/controllers/application_controller.rb2
-rw-r--r--app/models/project.rb4
-rw-r--r--app/models/repository.rb1
-rw-r--r--app/views/dashboard/groups/_groups.html.haml2
-rw-r--r--app/views/dashboard/groups/index.html.haml3
-rw-r--r--app/views/explore/groups/_groups.html.haml2
-rw-r--r--app/views/explore/groups/index.html.haml3
-rw-r--r--app/views/groups/_children.html.haml5
-rw-r--r--app/views/layouts/_head.html.haml1
-rw-r--r--app/views/shared/_group_form.html.haml2
-rw-r--r--changelogs/unreleased/24035-api-create-application.yml4
-rw-r--r--changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml5
-rw-r--r--changelogs/unreleased/42251-explicit-timezone-for-karma.yml5
-rw-r--r--changelogs/unreleased/dm-project-system-hooks-in-transaction.yml5
-rw-r--r--changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml5
-rw-r--r--config/karma.config.js2
-rw-r--r--config/webpack.config.js8
-rw-r--r--doc/api/applications.md37
-rw-r--r--doc/ci/variables/README.md2
-rw-r--r--doc/development/fe_guide/axios.md2
-rw-r--r--features/support/db_cleaner.rb2
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/applications.rb27
-rw-r--r--lib/api/entities.rb21
-rw-r--r--lib/gitlab/git/blob.rb22
-rw-r--r--lib/gitlab/git/repository.rb16
-rw-r--r--lib/gitlab/gitaly_client/blob_service.rb20
-rw-r--r--lib/gitlab/gitaly_client/conflicts_service.rb5
-rw-r--r--lib/gitlab/gitaly_client/repository_service.rb17
-rw-r--r--lib/gitlab/import_export/command_line_util.rb4
-rw-r--r--lib/gitlab/import_export/repo_saver.rb2
-rw-r--r--lib/gitlab/import_export/wiki_repo_saver.rb2
-rw-r--r--qa/qa/factory/resource/deploy_key.rb6
-rw-r--r--qa/qa/page/project/settings/deploy_keys.rb7
-rw-r--r--qa/qa/specs/features/project/add_deploy_key_spec.rb12
-rwxr-xr-xscripts/gitaly-test-build16
-rw-r--r--scripts/prepare_build.sh2
-rw-r--r--spec/features/issues/spam_issues_spec.rb3
-rw-r--r--spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb2
-rw-r--r--spec/javascripts/blob/notebook/index_spec.js6
-rw-r--r--spec/javascripts/boards/board_card_spec.js2
-rw-r--r--spec/javascripts/boards/board_list_spec.js2
-rw-r--r--spec/javascripts/boards/board_new_issue_spec.js2
-rw-r--r--spec/javascripts/boards/boards_store_spec.js2
-rw-r--r--spec/javascripts/boards/list_spec.js2
-rw-r--r--spec/javascripts/commit/pipelines/pipelines_spec.js5
-rw-r--r--spec/javascripts/issue_show/components/app_spec.js2
-rw-r--r--spec/javascripts/jobs/job_details_mediator_spec.js8
-rw-r--r--spec/javascripts/monitoring/dashboard_spec.js2
-rw-r--r--spec/javascripts/pipelines/pipelines_table_row_spec.js7
-rw-r--r--spec/javascripts/pipelines/pipelines_table_spec.js5
-rw-r--r--spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb2
-rw-r--r--spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb4
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb2
-rw-r--r--spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb43
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb28
-rw-r--r--spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb2
-rw-r--r--spec/migrations/calculate_conv_dev_index_percentages_spec.rb2
-rw-r--r--spec/migrations/fix_wrongly_renamed_routes_spec.rb2
-rw-r--r--spec/migrations/migrate_issues_to_ghost_user_spec.rb6
-rw-r--r--spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb2
-rw-r--r--spec/migrations/migrate_user_project_view_spec.rb2
-rw-r--r--spec/migrations/remove_duplicate_mr_events_spec.rb2
-rw-r--r--spec/migrations/rename_more_reserved_project_names_spec.rb4
-rw-r--r--spec/migrations/rename_reserved_project_names_spec.rb4
-rw-r--r--spec/migrations/rename_users_with_renamed_namespace_spec.rb2
-rw-r--r--spec/migrations/update_retried_for_ci_build_spec.rb2
-rw-r--r--spec/models/concerns/avatarable_spec.rb39
-rw-r--r--spec/models/member_spec.rb4
-rw-r--r--spec/models/project_group_link_spec.rb2
-rw-r--r--spec/models/project_spec.rb17
-rw-r--r--spec/models/repository_spec.rb8
-rw-r--r--spec/models/user_spec.rb2
-rw-r--r--spec/requests/api/applications_spec.rb86
-rw-r--r--spec/services/issues/move_service_spec.rb4
-rw-r--r--spec/support/db_cleaner.rb26
-rw-r--r--spec/support/features/discussion_comments_shared_example.rb6
-rw-r--r--spec/uploaders/job_artifact_uploader_spec.rb4
95 files changed, 606 insertions, 234 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index f9c5ebe7a35..349ea49fe8f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,7 +6,7 @@ image: "dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.3.6-golang-1.9-git
- gitlab-org
.default-cache: &default-cache
- key: "ruby-235-with-yarn"
+ key: "ruby-2.3.6-with-yarn"
paths:
- vendor/ruby
- .yarn-cache/
@@ -322,69 +322,69 @@ setup-test-env:
paths:
- tmp/tests
-rspec-pg 0 26: *rspec-metadata-pg
-rspec-pg 1 26: *rspec-metadata-pg
-rspec-pg 2 26: *rspec-metadata-pg
-rspec-pg 3 26: *rspec-metadata-pg
-rspec-pg 4 26: *rspec-metadata-pg
-rspec-pg 5 26: *rspec-metadata-pg
-rspec-pg 6 26: *rspec-metadata-pg
-rspec-pg 7 26: *rspec-metadata-pg
-rspec-pg 8 26: *rspec-metadata-pg
-rspec-pg 9 26: *rspec-metadata-pg
-rspec-pg 10 26: *rspec-metadata-pg
-rspec-pg 11 26: *rspec-metadata-pg
-rspec-pg 12 26: *rspec-metadata-pg
-rspec-pg 13 26: *rspec-metadata-pg
-rspec-pg 14 26: *rspec-metadata-pg
-rspec-pg 15 26: *rspec-metadata-pg
-rspec-pg 16 26: *rspec-metadata-pg
-rspec-pg 17 26: *rspec-metadata-pg
-rspec-pg 18 26: *rspec-metadata-pg
-rspec-pg 19 26: *rspec-metadata-pg
-rspec-pg 20 26: *rspec-metadata-pg
-rspec-pg 21 26: *rspec-metadata-pg
-rspec-pg 22 26: *rspec-metadata-pg
-rspec-pg 23 26: *rspec-metadata-pg
-rspec-pg 24 26: *rspec-metadata-pg
-rspec-pg 25 26: *rspec-metadata-pg
-
-rspec-mysql 0 26: *rspec-metadata-mysql
-rspec-mysql 1 26: *rspec-metadata-mysql
-rspec-mysql 2 26: *rspec-metadata-mysql
-rspec-mysql 3 26: *rspec-metadata-mysql
-rspec-mysql 4 26: *rspec-metadata-mysql
-rspec-mysql 5 26: *rspec-metadata-mysql
-rspec-mysql 6 26: *rspec-metadata-mysql
-rspec-mysql 7 26: *rspec-metadata-mysql
-rspec-mysql 8 26: *rspec-metadata-mysql
-rspec-mysql 9 26: *rspec-metadata-mysql
-rspec-mysql 10 26: *rspec-metadata-mysql
-rspec-mysql 11 26: *rspec-metadata-mysql
-rspec-mysql 12 26: *rspec-metadata-mysql
-rspec-mysql 13 26: *rspec-metadata-mysql
-rspec-mysql 14 26: *rspec-metadata-mysql
-rspec-mysql 15 26: *rspec-metadata-mysql
-rspec-mysql 16 26: *rspec-metadata-mysql
-rspec-mysql 17 26: *rspec-metadata-mysql
-rspec-mysql 18 26: *rspec-metadata-mysql
-rspec-mysql 19 26: *rspec-metadata-mysql
-rspec-mysql 20 26: *rspec-metadata-mysql
-rspec-mysql 21 26: *rspec-metadata-mysql
-rspec-mysql 22 26: *rspec-metadata-mysql
-rspec-mysql 23 26: *rspec-metadata-mysql
-rspec-mysql 24 26: *rspec-metadata-mysql
-rspec-mysql 25 26: *rspec-metadata-mysql
-
-spinach-pg 0 4: *spinach-metadata-pg
-spinach-pg 1 4: *spinach-metadata-pg
-spinach-pg 2 4: *spinach-metadata-pg
-spinach-pg 3 4: *spinach-metadata-pg
-
-spinach-mysql 0 4: *spinach-metadata-mysql
-spinach-mysql 1 4: *spinach-metadata-mysql
-spinach-mysql 2 4: *spinach-metadata-mysql
-spinach-mysql 3 4: *spinach-metadata-mysql
+rspec-pg 0 27: *rspec-metadata-pg
+rspec-pg 1 27: *rspec-metadata-pg
+rspec-pg 2 27: *rspec-metadata-pg
+rspec-pg 3 27: *rspec-metadata-pg
+rspec-pg 4 27: *rspec-metadata-pg
+rspec-pg 5 27: *rspec-metadata-pg
+rspec-pg 6 27: *rspec-metadata-pg
+rspec-pg 7 27: *rspec-metadata-pg
+rspec-pg 8 27: *rspec-metadata-pg
+rspec-pg 9 27: *rspec-metadata-pg
+rspec-pg 10 27: *rspec-metadata-pg
+rspec-pg 11 27: *rspec-metadata-pg
+rspec-pg 12 27: *rspec-metadata-pg
+rspec-pg 13 27: *rspec-metadata-pg
+rspec-pg 14 27: *rspec-metadata-pg
+rspec-pg 15 27: *rspec-metadata-pg
+rspec-pg 16 27: *rspec-metadata-pg
+rspec-pg 17 27: *rspec-metadata-pg
+rspec-pg 18 27: *rspec-metadata-pg
+rspec-pg 19 27: *rspec-metadata-pg
+rspec-pg 20 27: *rspec-metadata-pg
+rspec-pg 21 27: *rspec-metadata-pg
+rspec-pg 22 27: *rspec-metadata-pg
+rspec-pg 23 27: *rspec-metadata-pg
+rspec-pg 24 27: *rspec-metadata-pg
+rspec-pg 25 27: *rspec-metadata-pg
+rspec-pg 26 27: *rspec-metadata-pg
+
+rspec-mysql 0 27: *rspec-metadata-mysql
+rspec-mysql 1 27: *rspec-metadata-mysql
+rspec-mysql 2 27: *rspec-metadata-mysql
+rspec-mysql 3 27: *rspec-metadata-mysql
+rspec-mysql 4 27: *rspec-metadata-mysql
+rspec-mysql 5 27: *rspec-metadata-mysql
+rspec-mysql 6 27: *rspec-metadata-mysql
+rspec-mysql 7 27: *rspec-metadata-mysql
+rspec-mysql 8 27: *rspec-metadata-mysql
+rspec-mysql 9 27: *rspec-metadata-mysql
+rspec-mysql 10 27: *rspec-metadata-mysql
+rspec-mysql 11 27: *rspec-metadata-mysql
+rspec-mysql 12 27: *rspec-metadata-mysql
+rspec-mysql 13 27: *rspec-metadata-mysql
+rspec-mysql 14 27: *rspec-metadata-mysql
+rspec-mysql 15 27: *rspec-metadata-mysql
+rspec-mysql 16 27: *rspec-metadata-mysql
+rspec-mysql 17 27: *rspec-metadata-mysql
+rspec-mysql 18 27: *rspec-metadata-mysql
+rspec-mysql 19 27: *rspec-metadata-mysql
+rspec-mysql 20 27: *rspec-metadata-mysql
+rspec-mysql 21 27: *rspec-metadata-mysql
+rspec-mysql 22 27: *rspec-metadata-mysql
+rspec-mysql 23 27: *rspec-metadata-mysql
+rspec-mysql 24 27: *rspec-metadata-mysql
+rspec-mysql 25 27: *rspec-metadata-mysql
+rspec-mysql 26 27: *rspec-metadata-mysql
+
+spinach-pg 0 3: *spinach-metadata-pg
+spinach-pg 1 3: *spinach-metadata-pg
+spinach-pg 2 3: *spinach-metadata-pg
+
+spinach-mysql 0 3: *spinach-metadata-mysql
+spinach-mysql 1 3: *spinach-metadata-mysql
+spinach-mysql 2 3: *spinach-metadata-mysql
# Static analysis jobs
.ruby-static-analysis: &ruby-static-analysis
diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION
index 7375dee5f49..31b648bd6fa 100644
--- a/GITALY_SERVER_VERSION
+++ b/GITALY_SERVER_VERSION
@@ -1 +1 @@
-0.72.0
+0.73.0
diff --git a/Gemfile b/Gemfile
index a4c3db4bf5f..346182b3852 100644
--- a/Gemfile
+++ b/Gemfile
@@ -406,7 +406,7 @@ group :ed25519 do
end
# Gitaly GRPC client
-gem 'gitaly-proto', '~> 0.74.0', require: 'gitaly'
+gem 'gitaly-proto', '~> 0.76.0', require: 'gitaly'
gem 'toml-rb', '~> 0.3.15', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index d69c532b309..1cbeab8d6b5 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -285,7 +285,7 @@ GEM
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gherkin-ruby (0.3.2)
- gitaly-proto (0.74.0)
+ gitaly-proto (0.76.0)
google-protobuf (~> 3.1)
grpc (~> 1.0)
github-linguist (4.7.6)
@@ -1056,7 +1056,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.2.0)
- gitaly-proto (~> 0.74.0)
+ gitaly-proto (~> 0.76.0)
github-linguist (~> 4.7.0)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-markup (~> 1.6.2)
diff --git a/app/assets/javascripts/behaviors/copy_as_gfm.js b/app/assets/javascripts/behaviors/copy_as_gfm.js
index c6eca72c51b..ffe90595b5d 100644
--- a/app/assets/javascripts/behaviors/copy_as_gfm.js
+++ b/app/assets/javascripts/behaviors/copy_as_gfm.js
@@ -299,6 +299,13 @@ const gfmRules = {
export class CopyAsGFM {
constructor() {
+ // iOS currently does not support clipboardData.setData(). This bug should
+ // be fixed in iOS 12, but for now we'll disable this for all iOS browsers
+ // ref: https://trac.webkit.org/changeset/222228/webkit
+ const userAgent = (typeof navigator !== 'undefined' && navigator.userAgent) || '';
+ const isIOS = /\b(iPad|iPhone|iPod)(?=;)/.test(userAgent);
+ if (isIOS) return;
+
$(document).on('copy', '.md, .wiki', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
$(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
$(document).on('paste', '.js-gfm-input', CopyAsGFM.pasteGFM);
diff --git a/app/assets/javascripts/deploy_keys/components/app.vue b/app/assets/javascripts/deploy_keys/components/app.vue
index 7b68b19de75..5a782237b7d 100644
--- a/app/assets/javascripts/deploy_keys/components/app.vue
+++ b/app/assets/javascripts/deploy_keys/components/app.vue
@@ -87,6 +87,7 @@
<div v-else-if="hasKeys">
<keys-panel
title="Enabled deploy keys for this project"
+ class="qa-project-deploy-keys"
:keys="keys.enabled_keys"
:store="store"
:endpoint="endpoint"
diff --git a/app/assets/javascripts/dispatcher.js b/app/assets/javascripts/dispatcher.js
index d5659be28a4..89fc002d4ce 100644
--- a/app/assets/javascripts/dispatcher.js
+++ b/app/assets/javascripts/dispatcher.js
@@ -1,13 +1,8 @@
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-arrow-callback, wrap-iife, no-shadow, consistent-return, one-var, one-var-declaration-per-line, camelcase, default-case, no-new, quotes, no-duplicate-case, no-case-declarations, no-fallthrough, max-len */
-import notificationsDropdown from './notifications_dropdown';
-import LineHighlighter from './line_highlighter';
import MergeRequest from './merge_request';
import Flash from './flash';
-import BlobViewer from './blob/viewer/index';
import GfmAutoComplete from './gfm_auto_complete';
-import Star from './star';
import ZenMode from './zen_mode';
-import PerformanceBar from './performance_bar';
import initNotes from './init_notes';
import initIssuableSidebar from './init_issuable_sidebar';
import { convertPermissionToBoolean } from './lib/utils/common_utils';
@@ -538,6 +533,11 @@ import SearchAutocomplete from './search_autocomplete';
.then(callDefault)
.catch(fail);
break;
+ case 'dashboard:groups:index':
+ import('./pages/dashboard/groups/index')
+ .then(callDefault)
+ .catch(fail);
+ break;
}
switch (path[0]) {
case 'sessions':
@@ -621,23 +621,12 @@ import SearchAutocomplete from './search_autocomplete';
.then(callDefault)
.catch(fail);
break;
- case 'show':
- new Star();
- notificationsDropdown();
- break;
case 'wikis':
import('./pages/projects/wikis')
.then(callDefault)
.catch(fail);
shortcut_handler = true;
break;
- case 'snippets':
- if (path[2] === 'show') {
- new ZenMode();
- new LineHighlighter();
- new BlobViewer();
- }
- break;
}
break;
}
@@ -647,7 +636,9 @@ import SearchAutocomplete from './search_autocomplete';
}
if (document.querySelector('#peek')) {
- new PerformanceBar({ container: '#peek' });
+ import('./performance_bar')
+ .then(m => new m.default({ container: '#peek' })) // eslint-disable-line new-cap
+ .catch(fail);
}
};
diff --git a/app/assets/javascripts/groups/index.js b/app/assets/javascripts/groups/index.js
index 8b850765a1b..57eaac72906 100644
--- a/app/assets/javascripts/groups/index.js
+++ b/app/assets/javascripts/groups/index.js
@@ -10,7 +10,7 @@ import groupItemComponent from './components/group_item.vue';
Vue.use(Translate);
-document.addEventListener('DOMContentLoaded', () => {
+export default () => {
const el = document.getElementById('js-groups-tree');
// Don't do anything if element doesn't exist (No groups)
@@ -71,4 +71,4 @@ document.addEventListener('DOMContentLoaded', () => {
});
},
});
-});
+};
diff --git a/app/assets/javascripts/groups/service/groups_service.js b/app/assets/javascripts/groups/service/groups_service.js
index 639410384c2..b79ba291463 100644
--- a/app/assets/javascripts/groups/service/groups_service.js
+++ b/app/assets/javascripts/groups/service/groups_service.js
@@ -1,7 +1,5 @@
import Vue from 'vue';
-import VueResource from 'vue-resource';
-
-Vue.use(VueResource);
+import '../../vue_shared/vue_resource_interceptor';
export default class GroupsService {
constructor(endpoint) {
diff --git a/app/assets/javascripts/pages/dashboard/groups/index/index.js b/app/assets/javascripts/pages/dashboard/groups/index/index.js
new file mode 100644
index 00000000000..8a2aae706c0
--- /dev/null
+++ b/app/assets/javascripts/pages/dashboard/groups/index/index.js
@@ -0,0 +1,5 @@
+import initGroupsList from '../../../../groups';
+
+export default () => {
+ initGroupsList();
+};
diff --git a/app/assets/javascripts/pages/explore/groups/index.js b/app/assets/javascripts/pages/explore/groups/index.js
index 859b073f1cb..e59c38b8bc4 100644
--- a/app/assets/javascripts/pages/explore/groups/index.js
+++ b/app/assets/javascripts/pages/explore/groups/index.js
@@ -1,8 +1,10 @@
import GroupsList from '~/groups_list';
import Landing from '~/landing';
+import initGroupsList from '../../../groups';
export default function () {
new GroupsList(); // eslint-disable-line no-new
+ initGroupsList();
const landingElement = document.querySelector('.js-explore-groups-landing');
if (!landingElement) return;
const exploreGroupsLanding = new Landing(
diff --git a/app/assets/javascripts/pages/groups/show/index.js b/app/assets/javascripts/pages/groups/show/index.js
index 45e11b64306..6ed0f010f15 100644
--- a/app/assets/javascripts/pages/groups/show/index.js
+++ b/app/assets/javascripts/pages/groups/show/index.js
@@ -5,6 +5,7 @@ import notificationsDropdown from '~/notifications_dropdown';
import NotificationsForm from '~/notifications_form';
import ProjectsList from '~/projects_list';
import ShortcutsNavigation from '~/shortcuts_navigation';
+import initGroupsList from '../../../groups';
export default () => {
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
@@ -16,4 +17,6 @@ export default () => {
if (newGroupChildWrapper) {
new NewGroupChild(newGroupChildWrapper);
}
+
+ initGroupsList();
};
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index 92dc1e59651..55154cdddcb 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -5,8 +5,12 @@ import TreeView from '~/tree';
import BlobViewer from '~/blob/viewer/index';
import Activities from '~/activities';
import { ajaxGet } from '~/lib/utils/common_utils';
+import Star from '../../../star';
+import notificationsDropdown from '../../../notifications_dropdown';
export default () => {
+ new Star(); // eslint-disable-line no-new
+ notificationsDropdown();
new ShortcutsNavigation(); // eslint-disable-line no-new
new NotificationsForm(); // eslint-disable-line no-new
new UserCallout({ // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/projects/snippets/show/index.js b/app/assets/javascripts/pages/projects/snippets/show/index.js
index d8cf5184f8f..a3cf75c385b 100644
--- a/app/assets/javascripts/pages/projects/snippets/show/index.js
+++ b/app/assets/javascripts/pages/projects/snippets/show/index.js
@@ -1,7 +1,11 @@
import initNotes from '~/init_notes';
import ZenMode from '~/zen_mode';
+import LineHighlighter from '../../../../line_highlighter';
+import BlobViewer from '../../../../blob/viewer';
export default function () {
+ new LineHighlighter(); // eslint-disable-line no-new
+ new BlobViewer(); // eslint-disable-line no-new
initNotes();
new ZenMode(); // eslint-disable-line no-new
}
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index ee21d81f23e..95ad38d9230 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -147,6 +147,8 @@ class ApplicationController < ActionController::Base
format.html do
render file: Rails.root.join("public", "404"), layout: false, status: "404"
end
+ # Prevent the Rails CSRF protector from thinking a missing .js file is a JavaScript file
+ format.js { render json: '', status: :not_found, content_type: 'application/json' }
format.any { head :not_found }
end
end
diff --git a/app/models/project.rb b/app/models/project.rb
index 0570bbc8ee3..e19873f64ce 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -971,9 +971,9 @@ class Project < ActiveRecord::Base
hooks.hooks_for(hooks_scope).each do |hook|
hook.async_execute(data, hooks_scope.to_s)
end
- end
- SystemHooksService.new.execute_hooks(data, hooks_scope)
+ SystemHooksService.new.execute_hooks(data, hooks_scope)
+ end
end
def execute_services(data, hooks_scope = :push_hooks)
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 73c4899cb9b..824e18bec78 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -20,6 +20,7 @@ class Repository
attr_accessor :full_path, :disk_path, :project, :is_wiki
delegate :ref_name_for_sha, to: :raw_repository
+ delegate :bundle_to_disk, to: :raw_repository
CreateTreeError = Class.new(StandardError)
diff --git a/app/views/dashboard/groups/_groups.html.haml b/app/views/dashboard/groups/_groups.html.haml
index 601b6a8b1a7..db856ef7d7b 100644
--- a/app/views/dashboard/groups/_groups.html.haml
+++ b/app/views/dashboard/groups/_groups.html.haml
@@ -1,2 +1,4 @@
.js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'true', endpoint: dashboard_groups_path(format: :json), path: dashboard_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
+ .loading-container.text-center
+ = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index 25bf08c6c12..50f39f93283 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -3,9 +3,6 @@
- header_title "Groups", dashboard_groups_path
= render 'dashboard/groups_head'
-= webpack_bundle_tag 'common_vue'
-= webpack_bundle_tag 'groups'
-
- if params[:filter].blank? && @groups.empty?
= render 'shared/groups/empty_state'
- else
diff --git a/app/views/explore/groups/_groups.html.haml b/app/views/explore/groups/_groups.html.haml
index 91149498248..ff57b39e947 100644
--- a/app/views/explore/groups/_groups.html.haml
+++ b/app/views/explore/groups/_groups.html.haml
@@ -1,2 +1,4 @@
.js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'true', endpoint: explore_groups_path(format: :json), path: explore_groups_path, form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
+ .loading-container.text-center
+ = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 86abdf547cc..efa8b2706da 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -2,9 +2,6 @@
- page_title "Groups"
- header_title "Groups", dashboard_groups_path
-= webpack_bundle_tag 'common_vue'
-= webpack_bundle_tag 'groups'
-
- if current_user
= render 'dashboard/groups_head'
- else
diff --git a/app/views/groups/_children.html.haml b/app/views/groups/_children.html.haml
index 3afb6b2f849..742b40784d3 100644
--- a/app/views/groups/_children.html.haml
+++ b/app/views/groups/_children.html.haml
@@ -1,5 +1,4 @@
-= webpack_bundle_tag 'common_vue'
-= webpack_bundle_tag 'groups'
-
.js-groups-list-holder
#js-groups-tree{ data: { hide_projects: 'false', group_id: group.id, endpoint: group_children_path(group, format: :json), path: group_path(group), form_sel: 'form#group-filter-form', filter_sel: '.js-groups-list-filter', holder_sel: '.js-groups-list-holder', dropdown_sel: '.js-group-filter-dropdown-wrap' } }
+ .loading-container.text-center
+ = icon('spinner spin 2x', class: 'loading-animation prepend-top-20')
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index 1597621fa78..ea13a5e6d62 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -43,7 +43,6 @@
= webpack_bundle_tag "main"
= webpack_bundle_tag "raven" if current_application_settings.clientside_sentry_enabled
= webpack_bundle_tag "test" if Rails.env.test?
- = webpack_bundle_tag 'performance_bar' if performance_bar_enabled?
- if content_for?(:page_specific_javascripts)
= yield :page_specific_javascripts
diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml
index d0b9e891b82..cb21f90696f 100644
--- a/app/views/shared/_group_form.html.haml
+++ b/app/views/shared/_group_form.html.haml
@@ -1,5 +1,3 @@
-- content_for :page_specific_javascripts do
- = page_specific_javascript_bundle_tag('group')
- parent = @group.parent
- group_path = root_url
- group_path << parent.full_path + '/' if parent
diff --git a/changelogs/unreleased/24035-api-create-application.yml b/changelogs/unreleased/24035-api-create-application.yml
new file mode 100644
index 00000000000..c583a020d9d
--- /dev/null
+++ b/changelogs/unreleased/24035-api-create-application.yml
@@ -0,0 +1,4 @@
+---
+title: Add application create API
+merge_request: 8160
+author: Nicolas Merelli @PNSalocin
diff --git a/changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml b/changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml
new file mode 100644
index 00000000000..f4c44983736
--- /dev/null
+++ b/changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml
@@ -0,0 +1,5 @@
+---
+title: Fix copy/paste on iOS devices due to a bug in webkit
+merge_request: 15804
+author:
+type: fixed
diff --git a/changelogs/unreleased/42251-explicit-timezone-for-karma.yml b/changelogs/unreleased/42251-explicit-timezone-for-karma.yml
new file mode 100644
index 00000000000..25e0e774c48
--- /dev/null
+++ b/changelogs/unreleased/42251-explicit-timezone-for-karma.yml
@@ -0,0 +1,5 @@
+---
+title: Set timezone for karma to UTC
+merge_request: 16602
+author: Takuya Noguchi
+type: other
diff --git a/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml b/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml
new file mode 100644
index 00000000000..f59021c0ec9
--- /dev/null
+++ b/changelogs/unreleased/dm-project-system-hooks-in-transaction.yml
@@ -0,0 +1,5 @@
+---
+title: Execute system hooks after-commit when executing project hooks
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml b/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml
new file mode 100644
index 00000000000..3854985e576
--- /dev/null
+++ b/changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml
@@ -0,0 +1,5 @@
+---
+title: Return more consistent values for merge_status on MR APIs
+merge_request:
+author:
+type: fixed
diff --git a/config/karma.config.js b/config/karma.config.js
index 9f018d14b8f..a101d35704e 100644
--- a/config/karma.config.js
+++ b/config/karma.config.js
@@ -18,6 +18,8 @@ webpackConfig.devtool = 'cheap-inline-source-map';
// Karma configuration
module.exports = function(config) {
+ process.env.TZ = 'Etc/UTC';
+
var progressReporter = process.env.CI ? 'mocha' : 'progress';
var karmaConfig = {
diff --git a/config/webpack.config.js b/config/webpack.config.js
index 229db11acb2..783677b5b8d 100644
--- a/config/webpack.config.js
+++ b/config/webpack.config.js
@@ -44,9 +44,6 @@ var config = {
graphs: './graphs/graphs_bundle.js',
graphs_charts: './graphs/graphs_charts.js',
graphs_show: './graphs/graphs_show.js',
- group: './group.js',
- groups: './groups/index.js',
- groups_list: './groups_list.js',
help: './help/help.js',
how_to_merge: './how_to_merge.js',
issue_show: './issue_show/index.js',
@@ -85,7 +82,6 @@ var config = {
test: './test.js',
two_factor_auth: './two_factor_auth.js',
users: './users/index.js',
- performance_bar: './performance_bar.js',
webpack_runtime: './webpack.js',
},
@@ -119,9 +115,9 @@ var config = {
{
test: /\_worker\.js$/,
use: [
- {
+ {
loader: 'worker-loader',
- options: {
+ options: {
inline: true
}
},
diff --git a/doc/api/applications.md b/doc/api/applications.md
new file mode 100644
index 00000000000..933867ed0bb
--- /dev/null
+++ b/doc/api/applications.md
@@ -0,0 +1,37 @@
+# Applications API
+
+> [Introduced][ce-8160] in GitLab 10.5
+
+[ce-8160]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/8160
+
+## Create a application
+
+Create a application by posting a JSON payload.
+
+User must be admin to do that.
+
+Returns `200` if the request succeeds.
+
+```
+POST /applications
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `name` | string | yes | The name of the application |
+| `redirect_uri` | string | yes | The redirect URI of the application |
+| `scopes` | string | yes | The scopes of the application |
+
+```bash
+curl --request POST --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" --data "name=MyApplication&redirect_uri=http://redirect.uri&scopes=" https://gitlab.example.com/api/v3/applications
+```
+
+Example response:
+
+```json
+{
+ "application_id": "5832fc6e14300a0d962240a8144466eef4ee93ef0d218477e55f11cf12fc3737",
+ "secret": "ee1dd64b6adc89cf7e2c23099301ccc2c61b441064e9324d963c46902a85ec34",
+ "callback_url": "http://redirect.uri"
+}
+```
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index 06f69938aae..598a7515b01 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -110,7 +110,7 @@ future GitLab releases.**
| `CI_BUILD_MANUAL` | `CI_JOB_MANUAL` |
| `CI_BUILD_TOKEN` | `CI_JOB_TOKEN` |
-## `.gitlab-ci.yaml` defined variables
+## `.gitlab-ci.yml` defined variables
>**Note:**
This feature requires GitLab Runner 0.5.0 or higher and GitLab CI 7.14 or higher.
diff --git a/doc/development/fe_guide/axios.md b/doc/development/fe_guide/axios.md
index dcd8f5cb839..0d9397c3bd5 100644
--- a/doc/development/fe_guide/axios.md
+++ b/doc/development/fe_guide/axios.md
@@ -63,7 +63,7 @@ We have also decided against using [axios interceptors] because they are not sui
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
```
diff --git a/features/support/db_cleaner.rb b/features/support/db_cleaner.rb
index 8294bb1445f..31c922d23c3 100644
--- a/features/support/db_cleaner.rb
+++ b/features/support/db_cleaner.rb
@@ -1,6 +1,6 @@
require 'database_cleaner'
-DatabaseCleaner[:active_record].strategy = :truncation
+DatabaseCleaner[:active_record].strategy = :deletion
Spinach.hooks.before_scenario do
DatabaseCleaner.start
diff --git a/lib/api/api.rb b/lib/api/api.rb
index ae161efb358..f3f64244589 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -106,6 +106,7 @@ module API
# Keep in alphabetical order
mount ::API::AccessRequests
+ mount ::API::Applications
mount ::API::AwardEmoji
mount ::API::Boards
mount ::API::Branches
diff --git a/lib/api/applications.rb b/lib/api/applications.rb
new file mode 100644
index 00000000000..b122cdefe4e
--- /dev/null
+++ b/lib/api/applications.rb
@@ -0,0 +1,27 @@
+module API
+ # External applications API
+ class Applications < Grape::API
+ before { authenticated_as_admin! }
+
+ resource :applications do
+ desc 'Create a new application' do
+ detail 'This feature was introduced in GitLab 10.5'
+ success Entities::ApplicationWithSecret
+ end
+ params do
+ requires :name, type: String, desc: 'Application name'
+ requires :redirect_uri, type: String, desc: 'Application redirect URI'
+ requires :scopes, type: String, desc: 'Application scopes'
+ end
+ post do
+ application = Doorkeeper::Application.new(declared_params)
+
+ if application.save
+ present application, with: Entities::ApplicationWithSecret
+ else
+ render_validation_error! application
+ end
+ end
+ end
+ end
+end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 3f4b62dc1b2..5b470bd3479 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -507,7 +507,16 @@ module API
expose :work_in_progress?, as: :work_in_progress
expose :milestone, using: Entities::Milestone
expose :merge_when_pipeline_succeeds
- expose :merge_status
+
+ # Ideally we should deprecate `MergeRequest#merge_status` exposure and
+ # use `MergeRequest#mergeable?` instead (boolean).
+ # See https://gitlab.com/gitlab-org/gitlab-ce/issues/42344 for more
+ # information.
+ expose :merge_status do |merge_request|
+ # In order to avoid having a breaking change for users, we keep returning the
+ # expected values from MergeRequest#merge_status state machine.
+ merge_request.mergeable? ? 'can_be_merged' : 'cannot_be_merged'
+ end
expose :diff_head_sha, as: :sha
expose :merge_commit_sha
expose :user_notes_count
@@ -1157,5 +1166,15 @@ module API
pages_domain
end
end
+
+ class Application < Grape::Entity
+ expose :uid, as: :application_id
+ expose :redirect_uri, as: :callback_url
+ end
+
+ # Use with care, this exposes the secret
+ class ApplicationWithSecret < Application
+ expose :secret
+ end
end
end
diff --git a/lib/gitlab/git/blob.rb b/lib/gitlab/git/blob.rb
index f421bf69e8f..81e46028752 100644
--- a/lib/gitlab/git/blob.rb
+++ b/lib/gitlab/git/blob.rb
@@ -34,7 +34,7 @@ module Gitlab
def raw(repository, sha)
Gitlab::GitalyClient.migrate(:git_blob_raw) do |is_enabled|
if is_enabled
- Gitlab::GitalyClient::BlobService.new(repository).get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
+ repository.gitaly_blob_client.get_blob(oid: sha, limit: MAX_DATA_DISPLAY_SIZE)
else
rugged_raw(repository, sha, limit: MAX_DATA_DISPLAY_SIZE)
end
@@ -70,11 +70,19 @@ module Gitlab
# Returns array of Gitlab::Git::Blob
# Does not guarantee blob data will be set
def batch_lfs_pointers(repository, blob_ids)
- blob_ids.lazy
- .select { |sha| possible_lfs_blob?(repository, sha) }
- .map { |sha| rugged_raw(repository, sha, limit: LFS_POINTER_MAX_SIZE) }
- .select(&:lfs_pointer?)
- .force
+ return [] if blob_ids.empty?
+
+ repository.gitaly_migrate(:batch_lfs_pointers) do |is_enabled|
+ if is_enabled
+ repository.gitaly_blob_client.batch_lfs_pointers(blob_ids)
+ else
+ blob_ids.lazy
+ .select { |sha| possible_lfs_blob?(repository, sha) }
+ .map { |sha| rugged_raw(repository, sha, limit: LFS_POINTER_MAX_SIZE) }
+ .select(&:lfs_pointer?)
+ .force
+ end
+ end
end
def binary?(data)
@@ -258,7 +266,7 @@ module Gitlab
Gitlab::GitalyClient.migrate(:git_blob_load_all_data) do |is_enabled|
@data = begin
if is_enabled
- Gitlab::GitalyClient::BlobService.new(repository).get_blob(oid: id, limit: -1).data
+ repository.gitaly_blob_client.get_blob(oid: id, limit: -1).data
else
repository.lookup(id).content
end
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index d666362de0d..d7c712e75c5 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -1268,6 +1268,18 @@ module Gitlab
success || gitlab_projects_error
end
+ def bundle_to_disk(save_path)
+ gitaly_migrate(:bundle_to_disk) do |is_enabled|
+ if is_enabled
+ gitaly_repository_client.create_bundle(save_path)
+ else
+ run_git!(%W(bundle create #{save_path} --all))
+ end
+ end
+
+ true
+ end
+
# rubocop:disable Metrics/ParameterLists
def multi_action(
user, branch_name:, message:, actions:,
@@ -1319,6 +1331,10 @@ module Gitlab
@gitaly_remote_client ||= Gitlab::GitalyClient::RemoteService.new(self)
end
+ def gitaly_blob_client
+ @gitaly_blob_client ||= Gitlab::GitalyClient::BlobService.new(self)
+ end
+
def gitaly_conflicts_client(our_commit_oid, their_commit_oid)
Gitlab::GitalyClient::ConflictsService.new(self, our_commit_oid, their_commit_oid)
end
diff --git a/lib/gitlab/gitaly_client/blob_service.rb b/lib/gitlab/gitaly_client/blob_service.rb
index a250eb75bd4..ee36684197b 100644
--- a/lib/gitlab/gitaly_client/blob_service.rb
+++ b/lib/gitlab/gitaly_client/blob_service.rb
@@ -32,6 +32,26 @@ module Gitlab
binary: Gitlab::Git::Blob.binary?(data)
)
end
+
+ def batch_lfs_pointers(blob_ids)
+ request = Gitaly::GetLFSPointersRequest.new(
+ repository: @gitaly_repo,
+ blob_ids: blob_ids
+ )
+
+ response = GitalyClient.call(@gitaly_repo.storage_name, :blob_service, :get_lfs_pointers, request)
+
+ response.flat_map do |message|
+ message.lfs_pointers.map do |lfs_pointer|
+ Gitlab::Git::Blob.new(
+ id: lfs_pointer.oid,
+ size: lfs_pointer.size,
+ data: lfs_pointer.data,
+ binary: Gitlab::Git::Blob.binary?(lfs_pointer.data)
+ )
+ end
+ end
+ end
end
end
end
diff --git a/lib/gitlab/gitaly_client/conflicts_service.rb b/lib/gitlab/gitaly_client/conflicts_service.rb
index 2565d537aff..e14734495a8 100644
--- a/lib/gitlab/gitaly_client/conflicts_service.rb
+++ b/lib/gitlab/gitaly_client/conflicts_service.rb
@@ -25,6 +25,11 @@ module Gitlab
def conflicts?
list_conflict_files.any?
+ rescue GRPC::FailedPrecondition
+ # The server raises this exception when it encounters ConflictSideMissing, which
+ # means a conflict exists but its `theirs` or `ours` data is nil due to a non-existent
+ # file in one of the trees.
+ true
end
def resolve_conflicts(target_repository, resolution, source_branch, target_branch)
diff --git a/lib/gitlab/gitaly_client/repository_service.rb b/lib/gitlab/gitaly_client/repository_service.rb
index 12016aee2a6..654a3c314f1 100644
--- a/lib/gitlab/gitaly_client/repository_service.rb
+++ b/lib/gitlab/gitaly_client/repository_service.rb
@@ -161,6 +161,23 @@ module Gitlab
return response.error.b, 1
end
end
+
+ def create_bundle(save_path)
+ request = Gitaly::CreateBundleRequest.new(repository: @gitaly_repo)
+ response = GitalyClient.call(
+ @storage,
+ :repository_service,
+ :create_bundle,
+ request,
+ timeout: GitalyClient.default_timeout
+ )
+
+ File.open(save_path, 'wb') do |f|
+ response.each do |message|
+ f.write(message.data)
+ end
+ end
+ end
end
end
end
diff --git a/lib/gitlab/import_export/command_line_util.rb b/lib/gitlab/import_export/command_line_util.rb
index dd5d35feab9..25399f307f2 100644
--- a/lib/gitlab/import_export/command_line_util.rb
+++ b/lib/gitlab/import_export/command_line_util.rb
@@ -11,10 +11,6 @@ module Gitlab
untar_with_options(archive: archive, dir: dir, options: 'zxf')
end
- def git_bundle(repo_path:, bundle_path:)
- execute(%W(#{git_bin_path} --git-dir=#{repo_path} bundle create #{bundle_path} --all))
- end
-
def git_clone_bundle(repo_path:, bundle_path:)
execute(%W(#{git_bin_path} clone --bare -- #{bundle_path} #{repo_path}))
Gitlab::Git::Repository.create_hooks(repo_path, File.expand_path(Gitlab.config.gitlab_shell.hooks_path))
diff --git a/lib/gitlab/import_export/repo_saver.rb b/lib/gitlab/import_export/repo_saver.rb
index a7028a32570..695462c7dd2 100644
--- a/lib/gitlab/import_export/repo_saver.rb
+++ b/lib/gitlab/import_export/repo_saver.rb
@@ -21,7 +21,7 @@ module Gitlab
def bundle_to_disk
mkdir_p(@shared.export_path)
- git_bundle(repo_path: path_to_repo, bundle_path: @full_path)
+ @project.repository.bundle_to_disk(@full_path)
rescue => e
@shared.error(e)
false
diff --git a/lib/gitlab/import_export/wiki_repo_saver.rb b/lib/gitlab/import_export/wiki_repo_saver.rb
index 1e6722a7bba..5fa2e101e29 100644
--- a/lib/gitlab/import_export/wiki_repo_saver.rb
+++ b/lib/gitlab/import_export/wiki_repo_saver.rb
@@ -10,7 +10,7 @@ module Gitlab
def bundle_to_disk(full_path)
mkdir_p(@shared.export_path)
- git_bundle(repo_path: path_to_repo, bundle_path: full_path)
+ @wiki.repository.bundle_to_disk(full_path)
rescue => e
@shared.error(e)
false
diff --git a/qa/qa/factory/resource/deploy_key.rb b/qa/qa/factory/resource/deploy_key.rb
index 7c58e70bcc4..671114d38b1 100644
--- a/qa/qa/factory/resource/deploy_key.rb
+++ b/qa/qa/factory/resource/deploy_key.rb
@@ -4,6 +4,12 @@ module QA
class DeployKey < Factory::Base
attr_accessor :title, :key
+ product :title do
+ Page::Project::Settings::Repository.act do
+ expand_deploy_keys(&:key_title)
+ end
+ end
+
dependency Factory::Resource::Project, as: :project do |project|
project.name = 'project-to-deploy'
project.description = 'project for adding deploy key test'
diff --git a/qa/qa/page/project/settings/deploy_keys.rb b/qa/qa/page/project/settings/deploy_keys.rb
index bf42767c707..f9e40bf4252 100644
--- a/qa/qa/page/project/settings/deploy_keys.rb
+++ b/qa/qa/page/project/settings/deploy_keys.rb
@@ -10,6 +10,7 @@ module QA
view 'app/assets/javascripts/deploy_keys/components/app.vue' do
element :deploy_keys_section, /class=".*deploy\-keys.*"/
+ element :project_deploy_keys, 'class="qa-project-deploy-keys"'
end
view 'app/assets/javascripts/deploy_keys/components/key.vue' do
@@ -29,9 +30,9 @@ module QA
click_on 'Add key'
end
- def has_key_title?(title)
- page.within('.deploy-keys') do
- page.find('.title', text: title)
+ def key_title
+ page.within('.qa-project-deploy-keys') do
+ page.find('.title').text
end
end
end
diff --git a/qa/qa/specs/features/project/add_deploy_key_spec.rb b/qa/qa/specs/features/project/add_deploy_key_spec.rb
index 43a85213501..7a123e539e1 100644
--- a/qa/qa/specs/features/project/add_deploy_key_spec.rb
+++ b/qa/qa/specs/features/project/add_deploy_key_spec.rb
@@ -7,16 +7,12 @@ module QA
Runtime::Browser.visit(:gitlab, Page::Main::Login)
Page::Main::Login.act { sign_in_using_credentials }
- Factory::Resource::DeployKey.fabricate! do |deploy_key|
- deploy_key.title = deploy_key_title
- deploy_key.key = deploy_key_value
+ deploy_key = Factory::Resource::DeployKey.fabricate! do |resource|
+ resource.title = deploy_key_title
+ resource.key = deploy_key_value
end
- Page::Project::Settings::Repository.perform do |setting|
- setting.expand_deploy_keys do |page|
- expect(page).to have_key_title(deploy_key_title)
- end
- end
+ expect(deploy_key.title).to eq(deploy_key_title)
end
end
end
diff --git a/scripts/gitaly-test-build b/scripts/gitaly-test-build
index 95d9fe0f176..b42ae2a2595 100755
--- a/scripts/gitaly-test-build
+++ b/scripts/gitaly-test-build
@@ -9,11 +9,21 @@ require 'fileutils'
# called 'bundle install' using a different Gemfile, as happens with
# gitlab-ce and gitaly.
-dir = 'tmp/tests/gitaly'
+tmp_tests_gitaly_dir = File.expand_path('../tmp/tests/gitaly', __dir__)
-abort 'gitaly build failed' unless system('make', chdir: dir)
+# Use the top-level bundle vendor folder so that we don't reinstall gems twice
+bundle_vendor_path = File.expand_path('../vendor', __dir__)
+
+env = {
+ # This ensure the `clean` config set in `scripts/prepare_build.sh` isn't taken into account
+ 'BUNDLE_IGNORE_CONFIG' => 'true',
+ 'BUNDLE_GEMFILE' => File.join(tmp_tests_gitaly_dir, 'ruby', 'Gemfile'),
+ 'BUNDLE_FLAGS' => "--jobs=4 --path=#{bundle_vendor_path} --retry=3"
+}
+
+abort 'gitaly build failed' unless system(env, 'make', chdir: tmp_tests_gitaly_dir)
# Make the 'gitaly' executable look newer than 'GITALY_SERVER_VERSION'.
# Without this a gitaly executable created in the setup-test-env job
# will look stale compared to GITALY_SERVER_VERSION.
-FileUtils.touch(File.join(dir, 'gitaly'), mtime: Time.now + (1 << 24))
+FileUtils.touch(File.join(tmp_tests_gitaly_dir, 'gitaly'), mtime: Time.now + (1 << 24))
diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh
index ea406aadf39..206d62dbc78 100644
--- a/scripts/prepare_build.sh
+++ b/scripts/prepare_build.sh
@@ -3,7 +3,7 @@
export SETUP_DB=${SETUP_DB:-true}
export CREATE_DB_USER=${CREATE_DB_USER:-$SETUP_DB}
export USE_BUNDLE_INSTALL=${USE_BUNDLE_INSTALL:-true}
-export BUNDLE_INSTALL_FLAGS="--without production --jobs $(nproc) --path vendor --retry 3 --quiet"
+export BUNDLE_INSTALL_FLAGS="--without=production --jobs=$(nproc) --path=vendor --retry=3 --quiet"
if [ "$USE_BUNDLE_INSTALL" != "false" ]; then
bundle install --clean $BUNDLE_INSTALL_FLAGS && bundle check
diff --git a/spec/features/issues/spam_issues_spec.rb b/spec/features/issues/spam_issues_spec.rb
index 53706ef84bc..c7cfd01f588 100644
--- a/spec/features/issues/spam_issues_spec.rb
+++ b/spec/features/issues/spam_issues_spec.rb
@@ -34,6 +34,9 @@ describe 'New issue', :js do
click_button 'Submit issue'
+ # reCAPTCHA alerts when it can't contact the server, so just accept it and move on
+ page.driver.browser.switch_to.alert.accept
+
# it is impossible to test recaptcha automatically and there is no possibility to fill in recaptcha
# recaptcha verification is skipped in test environment and it always returns true
expect(page).not_to have_content('issue title')
diff --git a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
index 590210d44ef..3e83a549682 100644
--- a/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
+++ b/spec/features/merge_request/user_resolves_diff_notes_and_discussions_resolve_spec.rb
@@ -108,7 +108,7 @@ describe 'Merge request > User resolves diff notes and discussions', :js do
it 'shows resolved discussion when toggled' do
find(".timeline-content .discussion[data-discussion-id='#{note.discussion_id}'] .discussion-toggle-button").click
- expect(page.find(".timeline-content #note_#{note.noteable_id}")).to be_visible
+ expect(page.find(".timeline-content #note_#{note.id}")).to be_visible
end
end
diff --git a/spec/javascripts/blob/notebook/index_spec.js b/spec/javascripts/blob/notebook/index_spec.js
index df1b2c9960b..a143fc827d5 100644
--- a/spec/javascripts/blob/notebook/index_spec.js
+++ b/spec/javascripts/blob/notebook/index_spec.js
@@ -45,7 +45,7 @@ describe('iPython notebook renderer', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('does not show loading icon', () => {
@@ -96,7 +96,7 @@ describe('iPython notebook renderer', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('does not show loading icon', () => {
@@ -127,7 +127,7 @@ describe('iPython notebook renderer', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('does not show loading icon', () => {
diff --git a/spec/javascripts/boards/board_card_spec.js b/spec/javascripts/boards/board_card_spec.js
index 4e73fa1fe87..80a598e63bd 100644
--- a/spec/javascripts/boards/board_card_spec.js
+++ b/spec/javascripts/boards/board_card_spec.js
@@ -55,7 +55,7 @@ describe('Board card', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('returns false when detailIssue is empty', () => {
diff --git a/spec/javascripts/boards/board_list_spec.js b/spec/javascripts/boards/board_list_spec.js
index 9ffdac9be97..a5fcb10b9dd 100644
--- a/spec/javascripts/boards/board_list_spec.js
+++ b/spec/javascripts/boards/board_list_spec.js
@@ -60,7 +60,7 @@ describe('Board list component', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('renders component', () => {
diff --git a/spec/javascripts/boards/board_new_issue_spec.js b/spec/javascripts/boards/board_new_issue_spec.js
index c62c537841c..e204985f039 100644
--- a/spec/javascripts/boards/board_new_issue_spec.js
+++ b/spec/javascripts/boards/board_new_issue_spec.js
@@ -58,7 +58,7 @@ describe('Issue boards new issue form', () => {
afterEach(() => {
vm.$destroy();
- mock.reset();
+ mock.restore();
});
it('calls submit if submit button is clicked', (done) => {
diff --git a/spec/javascripts/boards/boards_store_spec.js b/spec/javascripts/boards/boards_store_spec.js
index 49fb20f4c84..8411f4dd8a6 100644
--- a/spec/javascripts/boards/boards_store_spec.js
+++ b/spec/javascripts/boards/boards_store_spec.js
@@ -35,7 +35,7 @@ describe('Store', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('starts with a blank state', () => {
diff --git a/spec/javascripts/boards/list_spec.js b/spec/javascripts/boards/list_spec.js
index e5e7b48228b..34964b20b05 100644
--- a/spec/javascripts/boards/list_spec.js
+++ b/spec/javascripts/boards/list_spec.js
@@ -30,7 +30,7 @@ describe('List model', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('gets issues when created', (done) => {
diff --git a/spec/javascripts/commit/pipelines/pipelines_spec.js b/spec/javascripts/commit/pipelines/pipelines_spec.js
index d62c2966a8b..0afe09d87bc 100644
--- a/spec/javascripts/commit/pipelines/pipelines_spec.js
+++ b/spec/javascripts/commit/pipelines/pipelines_spec.js
@@ -10,9 +10,10 @@ describe('Pipelines table in Commits and Merge requests', () => {
preloadFixtures(jsonFixtureName);
beforeEach(() => {
- PipelinesTable = Vue.extend(pipelinesTable);
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
- pipeline = pipelines.find(p => p.id === 1);
+
+ PipelinesTable = Vue.extend(pipelinesTable);
+ pipeline = pipelines.find(p => p.user !== null && p.commit !== null);
});
describe('successful request', () => {
diff --git a/spec/javascripts/issue_show/components/app_spec.js b/spec/javascripts/issue_show/components/app_spec.js
index 9280db072b3..1c9f48028f2 100644
--- a/spec/javascripts/issue_show/components/app_spec.js
+++ b/spec/javascripts/issue_show/components/app_spec.js
@@ -59,7 +59,7 @@ describe('Issuable output', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
realtimeRequestCount = 0;
vm.poll.stop();
diff --git a/spec/javascripts/jobs/job_details_mediator_spec.js b/spec/javascripts/jobs/job_details_mediator_spec.js
index 3069a0cd60e..ca5c9cf87e4 100644
--- a/spec/javascripts/jobs/job_details_mediator_spec.js
+++ b/spec/javascripts/jobs/job_details_mediator_spec.js
@@ -12,6 +12,10 @@ describe('JobMediator', () => {
mock = new MockAdapter(axios);
});
+ afterEach(() => {
+ mock.restore();
+ });
+
it('should set defaults', () => {
expect(mediator.store).toBeDefined();
expect(mediator.service).toBeDefined();
@@ -24,10 +28,6 @@ describe('JobMediator', () => {
mock.onGet().reply(200, job, {});
});
- afterEach(() => {
- mock.restore();
- });
-
it('should store received data', (done) => {
mediator.fetchJob();
setTimeout(() => {
diff --git a/spec/javascripts/monitoring/dashboard_spec.js b/spec/javascripts/monitoring/dashboard_spec.js
index 9885b8a790f..eb8f6bbe50d 100644
--- a/spec/javascripts/monitoring/dashboard_spec.js
+++ b/spec/javascripts/monitoring/dashboard_spec.js
@@ -38,7 +38,7 @@ describe('Dashboard', () => {
});
afterEach(() => {
- mock.reset();
+ mock.restore();
});
it('shows up a loading state', (done) => {
diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js
index a9126d2f4e9..b3cbf9aba48 100644
--- a/spec/javascripts/pipelines/pipelines_table_row_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js
@@ -24,9 +24,10 @@ describe('Pipelines Table Row', () => {
beforeEach(() => {
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
- pipeline = pipelines.find(p => p.id === 1);
- pipelineWithoutAuthor = pipelines.find(p => p.id === 2);
- pipelineWithoutCommit = pipelines.find(p => p.id === 3);
+
+ pipeline = pipelines.find(p => p.user !== null && p.commit !== null);
+ pipelineWithoutAuthor = pipelines.find(p => p.user == null && p.commit !== null);
+ pipelineWithoutCommit = pipelines.find(p => p.user == null && p.commit == null);
});
afterEach(() => {
diff --git a/spec/javascripts/pipelines/pipelines_table_spec.js b/spec/javascripts/pipelines/pipelines_table_spec.js
index ca2f9163313..4fc3c08145e 100644
--- a/spec/javascripts/pipelines/pipelines_table_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_spec.js
@@ -11,9 +11,10 @@ describe('Pipelines Table', () => {
preloadFixtures(jsonFixtureName);
beforeEach(() => {
- PipelinesTableComponent = Vue.extend(pipelinesTableComp);
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
- pipeline = pipelines.find(p => p.id === 1);
+
+ PipelinesTableComponent = Vue.extend(pipelinesTableComp);
+ pipeline = pipelines.find(p => p.user !== null && p.commit !== null);
});
describe('table', () => {
diff --git a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
index d21183b668b..c8df6dd2118 100644
--- a/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
+++ b/spec/lib/gitlab/background_migration/deserialize_merge_request_diffs_and_commits_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :truncate, :migration, schema: 20171114162227 do
+describe Gitlab::BackgroundMigration::DeserializeMergeRequestDiffsAndCommits, :migration, schema: 20171114162227 do
let(:merge_request_diffs) { table(:merge_request_diffs) }
let(:merge_requests) { table(:merge_requests) }
diff --git a/spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb b/spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb
index 7b5a00c6111..021e1d14b18 100644
--- a/spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb
+++ b/spec/lib/gitlab/background_migration/migrate_system_uploads_to_new_folder_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::BackgroundMigration::MigrateSystemUploadsToNewFolder do
+describe Gitlab::BackgroundMigration::MigrateSystemUploadsToNewFolder, :delete do
let(:migration) { described_class.new }
before do
@@ -8,7 +8,7 @@ describe Gitlab::BackgroundMigration::MigrateSystemUploadsToNewFolder do
end
describe '#perform' do
- it 'renames the path of system-uploads', :truncate do
+ it 'renames the path of system-uploads' do
upload = create(:upload, model: create(:project), path: 'uploads/system/project/avatar.jpg')
migration.perform('uploads/system/', 'uploads/-/system/')
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
index 596cc435bd9..cc7cb3f23fd 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_base_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :truncate do
+describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameBase, :delete do
let(:migration) { FakeRenameReservedPathMigrationV1.new }
let(:subject) { described_class.new(['the-path'], migration) }
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
index 1143182531f..f31475dbd71 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_namespaces_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :truncate do
+describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameNamespaces, :delete do
let(:migration) { FakeRenameReservedPathMigrationV1.new }
let(:subject) { described_class.new(['the-path'], migration) }
let(:namespace) { create(:group, name: 'the-path') }
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
index e850b5cd6a4..0958144643b 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1/rename_projects_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :truncate do
+describe Gitlab::Database::RenameReservedPathsMigration::V1::RenameProjects, :delete do
let(:migration) { FakeRenameReservedPathMigrationV1.new }
let(:subject) { described_class.new(['the-path'], migration) }
let(:project) do
diff --git a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
index 7695b95dc57..1d31f96159c 100644
--- a/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
+++ b/spec/lib/gitlab/database/rename_reserved_paths_migration/v1_spec.rb
@@ -13,7 +13,7 @@ shared_examples 'renames child namespaces' do |type|
end
end
-describe Gitlab::Database::RenameReservedPathsMigration::V1, :truncate do
+describe Gitlab::Database::RenameReservedPathsMigration::V1, :delete do
let(:subject) { FakeRenameReservedPathMigrationV1.new }
before do
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 8706c89c147..168207552ff 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -260,29 +260,42 @@ describe Gitlab::Git::Blob, seed_helper: true do
)
end
- it 'returns a list of Gitlab::Git::Blob' do
- blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
+ shared_examples 'fetching batch of LFS pointers' do
+ it 'returns a list of Gitlab::Git::Blob' do
+ blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
- expect(blobs.count).to eq(1)
- expect(blobs).to all( be_a(Gitlab::Git::Blob) )
- end
+ expect(blobs.count).to eq(1)
+ expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ end
- it 'silently ignores tree objects' do
- blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
+ it 'silently ignores tree objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
- expect(blobs).to eq([])
- end
+ expect(blobs).to eq([])
+ end
+
+ it 'silently ignores non lfs objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
- it 'silently ignores non lfs objects' do
- blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
+ expect(blobs).to eq([])
+ end
+
+ it 'avoids loading large blobs into memory' do
+ # This line could call `lookup` on `repository`, so do here before mocking.
+ non_lfs_blob_id = non_lfs_blob.id
+
+ expect(repository).not_to receive(:lookup)
- expect(blobs).to eq([])
+ described_class.batch_lfs_pointers(repository, [non_lfs_blob_id])
+ end
end
- it 'avoids loading large blobs into memory' do
- expect(repository).not_to receive(:lookup)
+ context 'when Gitaly batch_lfs_pointers is enabled' do
+ it_behaves_like 'fetching batch of LFS pointers'
+ end
- described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
+ context 'when Gitaly batch_lfs_pointers is disabled', :disable_gitaly do
+ it_behaves_like 'fetching batch of LFS pointers'
end
end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index aec7cde6df8..36ca3980de9 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1926,6 +1926,34 @@ describe Gitlab::Git::Repository, seed_helper: true do
it { expect(subject.repository_relative_path).to eq(repository.relative_path) }
end
+ describe '#bundle_to_disk' do
+ shared_examples 'bundling to disk' do
+ let(:save_path) { File.join(Dir.tmpdir, "repo-#{SecureRandom.hex}.bundle") }
+
+ after do
+ FileUtils.rm_rf(save_path)
+ end
+
+ it 'saves a bundle to disk' do
+ repository.bundle_to_disk(save_path)
+
+ success = system(
+ *%W(#{Gitlab.config.git.bin_path} -C #{repository.path} bundle verify #{save_path}),
+ [:out, :err] => '/dev/null'
+ )
+ expect(success).to be true
+ end
+ end
+
+ context 'when Gitaly bundle_to_disk feature is enabled' do
+ it_behaves_like 'bundling to disk'
+ end
+
+ context 'when Gitaly bundle_to_disk feature is disabled', :disable_gitaly do
+ it_behaves_like 'bundling to disk'
+ end
+ end
+
context 'gitlab_projects commands' do
let(:gitlab_projects) { repository.gitlab_projects }
let(:timeout) { Gitlab.config.gitlab_shell.git_timeout }
diff --git a/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb b/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
index 84c2e9f7e52..63defcb39bf 100644
--- a/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
+++ b/spec/migrations/add_head_pipeline_for_each_merge_request_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170508170547_add_head_pipeline_for_each_merge_request.rb')
-describe AddHeadPipelineForEachMergeRequest, :truncate do
+describe AddHeadPipelineForEachMergeRequest, :delete do
include ProjectForksHelper
let(:migration) { described_class.new }
diff --git a/spec/migrations/calculate_conv_dev_index_percentages_spec.rb b/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
index 597d8eab51c..f3a46025376 100644
--- a/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
+++ b/spec/migrations/calculate_conv_dev_index_percentages_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170803090603_calculate_conv_dev_index_percentages.rb')
-describe CalculateConvDevIndexPercentages, truncate: true do
+describe CalculateConvDevIndexPercentages, :delete do
let(:migration) { described_class.new }
let!(:conv_dev_index) do
create(:conversational_development_index_metric,
diff --git a/spec/migrations/fix_wrongly_renamed_routes_spec.rb b/spec/migrations/fix_wrongly_renamed_routes_spec.rb
index 78f8ab7512d..543cf55f076 100644
--- a/spec/migrations/fix_wrongly_renamed_routes_spec.rb
+++ b/spec/migrations/fix_wrongly_renamed_routes_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170518231126_fix_wrongly_renamed_routes.rb')
-describe FixWronglyRenamedRoutes, :truncate, :migration do
+describe FixWronglyRenamedRoutes, :migration do
let(:subject) { described_class.new }
let(:namespaces_table) { table(:namespaces) }
let(:projects_table) { table(:projects) }
diff --git a/spec/migrations/migrate_issues_to_ghost_user_spec.rb b/spec/migrations/migrate_issues_to_ghost_user_spec.rb
index cfd4021fbac..ff0d44e1ed2 100644
--- a/spec/migrations/migrate_issues_to_ghost_user_spec.rb
+++ b/spec/migrations/migrate_issues_to_ghost_user_spec.rb
@@ -8,10 +8,10 @@ describe MigrateIssuesToGhostUser, :migration do
let(:users) { table(:users) }
before do
- projects.create!(name: 'gitlab')
+ project = projects.create!(name: 'gitlab')
user = users.create(email: 'test@example.com')
- issues.create(title: 'Issue 1', author_id: nil, project_id: 1)
- issues.create(title: 'Issue 2', author_id: user.id, project_id: 1)
+ issues.create(title: 'Issue 1', author_id: nil, project_id: project.id)
+ issues.create(title: 'Issue 2', author_id: user.id, project_id: project.id)
end
context 'when ghost user exists' do
diff --git a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb b/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb
index 063829be546..a17c9c72bde 100644
--- a/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb
+++ b/spec/migrations/migrate_user_activities_to_users_last_activity_on_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170324160416_migrate_user_activities_to_users_last_activity_on.rb')
-describe MigrateUserActivitiesToUsersLastActivityOn, :clean_gitlab_redis_shared_state, :truncate do
+describe MigrateUserActivitiesToUsersLastActivityOn, :clean_gitlab_redis_shared_state, :delete do
let(:migration) { described_class.new }
let!(:user_active_1) { create(:user) }
let!(:user_active_2) { create(:user) }
diff --git a/spec/migrations/migrate_user_project_view_spec.rb b/spec/migrations/migrate_user_project_view_spec.rb
index 5e16769d63a..31d16e17d7b 100644
--- a/spec/migrations/migrate_user_project_view_spec.rb
+++ b/spec/migrations/migrate_user_project_view_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170406142253_migrate_user_project_view.rb')
-describe MigrateUserProjectView, :truncate do
+describe MigrateUserProjectView, :delete do
let(:migration) { described_class.new }
let!(:user) { create(:user, project_view: 'readme') }
diff --git a/spec/migrations/remove_duplicate_mr_events_spec.rb b/spec/migrations/remove_duplicate_mr_events_spec.rb
index e393374028f..e51872239ad 100644
--- a/spec/migrations/remove_duplicate_mr_events_spec.rb
+++ b/spec/migrations/remove_duplicate_mr_events_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170815060945_remove_duplicate_mr_events.rb')
-describe RemoveDuplicateMrEvents, truncate: true do
+describe RemoveDuplicateMrEvents, :delete do
let(:migration) { described_class.new }
describe '#up' do
diff --git a/spec/migrations/rename_more_reserved_project_names_spec.rb b/spec/migrations/rename_more_reserved_project_names_spec.rb
index ae3a4cb9b29..75310075cc5 100644
--- a/spec/migrations/rename_more_reserved_project_names_spec.rb
+++ b/spec/migrations/rename_more_reserved_project_names_spec.rb
@@ -5,8 +5,8 @@ require Rails.root.join('db', 'post_migrate', '20170313133418_rename_more_reserv
# This migration uses multiple threads, and thus different transactions. This
# means data created in this spec may not be visible to some threads. To work
-# around this we use the TRUNCATE cleaning strategy.
-describe RenameMoreReservedProjectNames, truncate: true do
+# around this we use the DELETE cleaning strategy.
+describe RenameMoreReservedProjectNames, :delete do
let(:migration) { described_class.new }
let!(:project) { create(:project) }
diff --git a/spec/migrations/rename_reserved_project_names_spec.rb b/spec/migrations/rename_reserved_project_names_spec.rb
index 462f4c08d63..e6555b1fe6b 100644
--- a/spec/migrations/rename_reserved_project_names_spec.rb
+++ b/spec/migrations/rename_reserved_project_names_spec.rb
@@ -5,8 +5,8 @@ require Rails.root.join('db', 'post_migrate', '20161221153951_rename_reserved_pr
# This migration uses multiple threads, and thus different transactions. This
# means data created in this spec may not be visible to some threads. To work
-# around this we use the TRUNCATE cleaning strategy.
-describe RenameReservedProjectNames, truncate: true do
+# around this we use the DELETE cleaning strategy.
+describe RenameReservedProjectNames, :delete do
let(:migration) { described_class.new }
let!(:project) { create(:project) }
diff --git a/spec/migrations/rename_users_with_renamed_namespace_spec.rb b/spec/migrations/rename_users_with_renamed_namespace_spec.rb
index 1e9aab3d9a1..cbc0ebeb44d 100644
--- a/spec/migrations/rename_users_with_renamed_namespace_spec.rb
+++ b/spec/migrations/rename_users_with_renamed_namespace_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170518200835_rename_users_with_renamed_namespace.rb')
-describe RenameUsersWithRenamedNamespace, truncate: true do
+describe RenameUsersWithRenamedNamespace, :delete do
it 'renames a user that had their namespace renamed to the namespace path' do
other_user = create(:user, username: 'kodingu')
other_user1 = create(:user, username: 'api0')
diff --git a/spec/migrations/update_retried_for_ci_build_spec.rb b/spec/migrations/update_retried_for_ci_build_spec.rb
index 3742b4dafe5..ccb77766b84 100644
--- a/spec/migrations/update_retried_for_ci_build_spec.rb
+++ b/spec/migrations/update_retried_for_ci_build_spec.rb
@@ -1,7 +1,7 @@
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20170503004427_update_retried_for_ci_build.rb')
-describe UpdateRetriedForCiBuild, truncate: true do
+describe UpdateRetriedForCiBuild, :delete do
let(:pipeline) { create(:ci_pipeline) }
let!(:build_old) { create(:ci_build, pipeline: pipeline, name: 'test') }
let!(:build_new) { create(:ci_build, pipeline: pipeline, name: 'test') }
diff --git a/spec/models/concerns/avatarable_spec.rb b/spec/models/concerns/avatarable_spec.rb
index cbdc438be0b..3696e6f62fd 100644
--- a/spec/models/concerns/avatarable_spec.rb
+++ b/spec/models/concerns/avatarable_spec.rb
@@ -1,11 +1,11 @@
require 'spec_helper'
describe Avatarable do
- subject { create(:project, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) }
+ set(:project) { create(:project, avatar: fixture_file_upload(File.join(Rails.root, 'spec/fixtures/dk.png'))) }
let(:gitlab_host) { "https://gitlab.example.com" }
let(:relative_url_root) { "/gitlab" }
- let(:asset_host) { "https://gitlab-assets.example.com" }
+ let(:asset_host) { 'https://gitlab-assets.example.com' }
before do
stub_config_setting(base_url: gitlab_host)
@@ -15,29 +15,32 @@ describe Avatarable do
describe '#avatar_path' do
using RSpec::Parameterized::TableSyntax
- where(:has_asset_host, :visibility_level, :only_path, :avatar_path) do
- true | Project::PRIVATE | true | [gitlab_host, relative_url_root, subject.avatar.url]
- true | Project::PRIVATE | false | [gitlab_host, relative_url_root, subject.avatar.url]
- true | Project::INTERNAL | true | [gitlab_host, relative_url_root, subject.avatar.url]
- true | Project::INTERNAL | false | [gitlab_host, relative_url_root, subject.avatar.url]
- true | Project::PUBLIC | true | [subject.avatar.url]
- true | Project::PUBLIC | false | [asset_host, subject.avatar.url]
- false | Project::PRIVATE | true | [relative_url_root, subject.avatar.url]
- false | Project::PRIVATE | false | [gitlab_host, relative_url_root, subject.avatar.url]
- false | Project::INTERNAL | true | [relative_url_root, subject.avatar.url]
- false | Project::INTERNAL | false | [gitlab_host, relative_url_root, subject.avatar.url]
- false | Project::PUBLIC | true | [relative_url_root, subject.avatar.url]
- false | Project::PUBLIC | false | [gitlab_host, relative_url_root, subject.avatar.url]
+ where(:has_asset_host, :visibility_level, :only_path, :avatar_path_prefix) do
+ true | Project::PRIVATE | true | [gitlab_host, relative_url_root]
+ true | Project::PRIVATE | false | [gitlab_host, relative_url_root]
+ true | Project::INTERNAL | true | [gitlab_host, relative_url_root]
+ true | Project::INTERNAL | false | [gitlab_host, relative_url_root]
+ true | Project::PUBLIC | true | []
+ true | Project::PUBLIC | false | [asset_host]
+ false | Project::PRIVATE | true | [relative_url_root]
+ false | Project::PRIVATE | false | [gitlab_host, relative_url_root]
+ false | Project::INTERNAL | true | [relative_url_root]
+ false | Project::INTERNAL | false | [gitlab_host, relative_url_root]
+ false | Project::PUBLIC | true | [relative_url_root]
+ false | Project::PUBLIC | false | [gitlab_host, relative_url_root]
end
with_them do
before do
- allow(ActionController::Base).to receive(:asset_host).and_return(has_asset_host ? asset_host : nil)
- subject.visibility_level = visibility_level
+ allow(ActionController::Base).to receive(:asset_host) { has_asset_host && asset_host }
+
+ project.visibility_level = visibility_level
end
+ let(:avatar_path) { (avatar_path_prefix + [project.avatar.url]).join }
+
it 'returns the expected avatar path' do
- expect(subject.avatar_path(only_path: only_path)).to eq(avatar_path.join)
+ expect(project.avatar_path(only_path: only_path)).to eq(avatar_path)
end
end
end
diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb
index 6aa0e7f49c3..c64cdf8f812 100644
--- a/spec/models/member_spec.rb
+++ b/spec/models/member_spec.rb
@@ -488,7 +488,7 @@ describe Member do
member.accept_invite!(user)
end
- it "refreshes user's authorized projects", :truncate do
+ it "refreshes user's authorized projects", :delete do
project = member.source
expect(user.authorized_projects).not_to include(project)
@@ -523,7 +523,7 @@ describe Member do
end
end
- describe "destroying a record", :truncate do
+ describe "destroying a record", :delete do
it "refreshes user's authorized projects" do
project = create(:project, :private)
user = create(:user)
diff --git a/spec/models/project_group_link_spec.rb b/spec/models/project_group_link_spec.rb
index 41e2ab20d69..1fccf92627a 100644
--- a/spec/models/project_group_link_spec.rb
+++ b/spec/models/project_group_link_spec.rb
@@ -30,7 +30,7 @@ describe ProjectGroupLink do
end
end
- describe "destroying a record", :truncate do
+ describe "destroying a record", :delete do
it "refreshes group users' authorized projects" do
project = create(:project, :private)
group = create(:group)
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index 4d10df410ab..31dcb543cbd 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -3228,5 +3228,22 @@ describe Project do
project = build(:project)
project.execute_hooks({ data: 'data' }, :merge_request_hooks)
end
+
+ it 'executes the system hooks when inside a transaction' do
+ allow_any_instance_of(WebHookService).to receive(:execute)
+
+ create(:system_hook, merge_requests_events: true)
+
+ project = build(:project)
+
+ # Ideally, we'd test that `WebHookWorker.jobs.size` increased by 1,
+ # but since the entire spec run takes place in a transaction, we never
+ # actually get to the `after_commit` hook that queues these jobs.
+ expect do
+ project.transaction do
+ project.execute_hooks({ data: 'data' }, :merge_request_hooks)
+ end
+ end.not_to raise_error # Sidekiq::Worker::EnqueueFromTransactionError
+ end
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index baaa9e3ef44..8f406253f39 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -365,12 +365,18 @@ describe Repository do
it { is_expected.to be_truthy }
end
- context 'non-mergeable branches' do
+ context 'non-mergeable branches without conflict sides missing' do
subject { repository.can_be_merged?('bb5206fee213d983da88c47f9cf4cc6caf9c66dc', 'feature') }
it { is_expected.to be_falsey }
end
+ context 'non-mergeable branches with conflict sides missing' do
+ subject { repository.can_be_merged?('conflict-missing-side', 'conflict-start') }
+
+ it { is_expected.to be_falsey }
+ end
+
context 'non merged branch' do
subject { repository.merged_to_root_ref?('fix') }
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 762cec9b95e..594f23718da 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1569,7 +1569,7 @@ describe User do
it { is_expected.to eq([private_group]) }
end
- describe '#authorized_projects', :truncate do
+ describe '#authorized_projects', :delete do
context 'with a minimum access level' do
it 'includes projects for which the user is an owner' do
user = create(:user)
diff --git a/spec/requests/api/applications_spec.rb b/spec/requests/api/applications_spec.rb
new file mode 100644
index 00000000000..f56bc932f40
--- /dev/null
+++ b/spec/requests/api/applications_spec.rb
@@ -0,0 +1,86 @@
+require 'spec_helper'
+
+describe API::Applications, :api do
+ include ApiHelpers
+
+ let(:admin_user) { create(:user, admin: true) }
+ let(:user) { create(:user, admin: false) }
+
+ describe 'POST /applications' do
+ context 'authenticated and authorized user' do
+ it 'creates and returns an OAuth application' do
+ expect do
+ post api('/applications', admin_user), name: 'application_name', redirect_uri: 'http://application.url', scopes: ''
+ end.to change { Doorkeeper::Application.count }.by 1
+
+ application = Doorkeeper::Application.find_by(name: 'application_name', redirect_uri: 'http://application.url')
+
+ expect(response).to have_http_status 201
+ expect(json_response).to be_a Hash
+ expect(json_response['application_id']).to eq application.uid
+ expect(json_response['secret']).to eq application.secret
+ expect(json_response['callback_url']).to eq application.redirect_uri
+ end
+
+ it 'does not allow creating an application with the wrong redirect_uri format' do
+ expect do
+ post api('/applications', admin_user), name: 'application_name', redirect_uri: 'wrong_url_format', scopes: ''
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 400
+ expect(json_response).to be_a Hash
+ expect(json_response['message']['redirect_uri'][0]).to eq('must be an absolute URI.')
+ end
+
+ it 'does not allow creating an application without a name' do
+ expect do
+ post api('/applications', admin_user), redirect_uri: 'http://application.url', scopes: ''
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 400
+ expect(json_response).to be_a Hash
+ expect(json_response['error']).to eq('name is missing')
+ end
+
+ it 'does not allow creating an application without a redirect_uri' do
+ expect do
+ post api('/applications', admin_user), name: 'application_name', scopes: ''
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 400
+ expect(json_response).to be_a Hash
+ expect(json_response['error']).to eq('redirect_uri is missing')
+ end
+
+ it 'does not allow creating an application without scopes' do
+ expect do
+ post api('/applications', admin_user), name: 'application_name', redirect_uri: 'http://application.url'
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 400
+ expect(json_response).to be_a Hash
+ expect(json_response['error']).to eq('scopes is missing')
+ end
+ end
+
+ context 'authorized user without authorization' do
+ it 'does not create application' do
+ expect do
+ post api('/applications', user), name: 'application_name', redirect_uri: 'http://application.url', scopes: ''
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 403
+ end
+ end
+
+ context 'non-authenticated user' do
+ it 'does not create application' do
+ expect do
+ post api('/applications'), name: 'application_name', redirect_uri: 'http://application.url'
+ end.not_to change { Doorkeeper::Application.count }
+
+ expect(response).to have_http_status 401
+ end
+ end
+ end
+end
diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb
index f3c98fa5416..388c9d63c7b 100644
--- a/spec/services/issues/move_service_spec.rb
+++ b/spec/services/issues/move_service_spec.rb
@@ -297,9 +297,11 @@ describe Issues::MoveService do
end
context 'project issue hooks' do
- let(:hook) { create(:project_hook, project: old_project, issues_events: true) }
+ let!(:hook) { create(:project_hook, project: old_project, issues_events: true) }
it 'executes project issue hooks' do
+ allow_any_instance_of(WebHookService).to receive(:execute)
+
# Ideally, we'd test that `WebHookWorker.jobs.size` increased by 1,
# but since the entire spec run takes place in a transaction, we never
# actually get to the `after_commit` hook that queues these jobs.
diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb
index edaee03ea6c..1809ae1d141 100644
--- a/spec/support/db_cleaner.rb
+++ b/spec/support/db_cleaner.rb
@@ -1,10 +1,26 @@
+require 'database_cleaner/active_record/deletion'
+
+module FakeInformationSchema
+ # Work around a bug in DatabaseCleaner when using the deletion strategy:
+ # https://github.com/DatabaseCleaner/database_cleaner/issues/347
+ #
+ # On MySQL, if the information schema is said to exist, we use an inaccurate
+ # row count leading to some tables not being cleaned when they should
+ def information_schema_exists?(_connection)
+ false
+ end
+end
+
+DatabaseCleaner::ActiveRecord::Deletion.prepend(FakeInformationSchema)
+
RSpec.configure do |config|
+ # Ensure all sequences are reset at the start of the suite run
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.append_after(:context) do
- DatabaseCleaner.clean_with(:truncation, cache_tables: false)
+ DatabaseCleaner.clean_with(:deletion, cache_tables: false)
end
config.before(:each) do
@@ -12,15 +28,15 @@ RSpec.configure do |config|
end
config.before(:each, :js) do
- DatabaseCleaner.strategy = :truncation
+ DatabaseCleaner.strategy = :deletion
end
- config.before(:each, :truncate) do
- DatabaseCleaner.strategy = :truncation
+ config.before(:each, :delete) do
+ DatabaseCleaner.strategy = :deletion
end
config.before(:each, :migration) do
- DatabaseCleaner.strategy = :truncation, { cache_tables: false }
+ DatabaseCleaner.strategy = :deletion, { cache_tables: false }
end
config.before(:each) do
diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb
index fa94aa2ae3d..c8662d41769 100644
--- a/spec/support/features/discussion_comments_shared_example.rb
+++ b/spec/support/features/discussion_comments_shared_example.rb
@@ -143,15 +143,17 @@ shared_examples 'discussion comments' do |resource_name|
end
if resource_name == 'merge request'
+ let(:note_id) { find("#{comments_selector} .note", match: :first)['data-note-id'] }
+
it 'shows resolved discussion when toggled' do
click_button "Resolve discussion"
- expect(page).to have_selector('.note-row-1', visible: true)
+ expect(page).to have_selector(".note-row-#{note_id}", visible: true)
refresh
click_button "Toggle discussion"
- expect(page).to have_selector('.note-row-1', visible: true)
+ expect(page).to have_selector(".note-row-#{note_id}", visible: true)
end
end
end
diff --git a/spec/uploaders/job_artifact_uploader_spec.rb b/spec/uploaders/job_artifact_uploader_spec.rb
index 14fd5f3600f..98a4373e9d0 100644
--- a/spec/uploaders/job_artifact_uploader_spec.rb
+++ b/spec/uploaders/job_artifact_uploader_spec.rb
@@ -8,7 +8,7 @@ describe JobArtifactUploader do
describe '#store_dir' do
subject { uploader.store_dir }
- let(:path) { "#{job_artifact.created_at.utc.strftime('%Y_%m_%d')}/#{job_artifact.project_id}/#{job_artifact.id}" }
+ let(:path) { "#{job_artifact.created_at.utc.strftime('%Y_%m_%d')}/#{job_artifact.job_id}/#{job_artifact.id}" }
context 'when using local storage' do
it { is_expected.to start_with(local_path) }
@@ -45,7 +45,7 @@ describe JobArtifactUploader do
it { is_expected.to start_with(local_path) }
it { is_expected.to include("/#{job_artifact.created_at.utc.strftime('%Y_%m_%d')}/") }
- it { is_expected.to include("/#{job_artifact.project_id}/") }
+ it { is_expected.to include("/#{job_artifact.job_id}/#{job_artifact.id}/") }
it { is_expected.to end_with("ci_build_artifacts.zip") }
end
end