summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml1
-rw-r--r--CHANGELOG.md16
-rw-r--r--Gemfile2
-rw-r--r--Gemfile.lock2
-rw-r--r--app/assets/javascripts/awards_handler.js12
-rw-r--r--app/assets/javascripts/blob_edit/edit_blob.js15
-rw-r--r--app/assets/javascripts/compare.js47
-rw-r--r--app/assets/javascripts/compare_autocomplete.js16
-rw-r--r--app/assets/javascripts/create_merge_request_dropdown.js147
-rw-r--r--app/assets/javascripts/dropzone_input.js33
-rw-r--r--app/assets/javascripts/due_date_select.js48
-rw-r--r--app/assets/javascripts/filterable_list.js31
-rw-r--r--app/assets/javascripts/groups/groups_filterable_list.js17
-rw-r--r--app/assets/javascripts/monitoring/components/dashboard.vue8
-rw-r--r--app/assets/javascripts/monitoring/components/empty_state.vue11
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/assignees.js2
-rw-r--r--app/assets/javascripts/users_select.js6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js1
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js139
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue192
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/dependencies.js2
-rw-r--r--app/helpers/application_helper.rb2
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--app/models/merge_request.rb12
-rw-r--r--app/models/project_services/jira_service.rb2
-rw-r--r--app/services/merge_requests/refresh_service.rb16
-rw-r--r--app/views/projects/_last_push.html.haml2
-rw-r--r--app/views/shared/form_elements/_description.html.haml2
-rw-r--r--app/views/shared/issuable/_form.html.haml2
-rw-r--r--app/views/shared/issuable/form/_title.html.haml2
-rwxr-xr-xbin/upgrade.rb3
-rw-r--r--changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml5
-rw-r--r--changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml5
-rw-r--r--changelogs/unreleased/42022-allow-users-to-request-access-not-visible-when-project-visibility-is-public.yml5
-rw-r--r--changelogs/unreleased/42161-gitaly-commitservice-encoding-undefinedconversionerror-u-c124-from-utf-8-to-ascii-8bit.yml5
-rw-r--r--changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml5
-rw-r--r--changelogs/unreleased/42327-import-from-gitlab-com-fails-destination-already-exists-and-is-not-an-empty-directory-error.yml6
-rw-r--r--changelogs/unreleased/fix-postgresql-table-grant.yml5
-rw-r--r--changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml5
-rw-r--r--changelogs/unreleased/osw-updates-merge-status-on-api-actions.yml5
-rw-r--r--changelogs/unreleased/sh-fix-jira-trailing-slash.yml5
-rw-r--r--config/application.rb1
-rw-r--r--doc/raketasks/backup_restore.md20
-rw-r--r--lib/api/entities.rb10
-rw-r--r--lib/gitlab/git/blame.rb4
-rw-r--r--lib/gitlab/git/popen.rb2
-rw-r--r--lib/gitlab/git/repository.rb58
-rw-r--r--lib/gitlab/popen.rb27
-rw-r--r--lib/gitlab/popen/runner.rb46
-rw-r--r--lib/gitlab/redis/cache.rb2
-rw-r--r--lib/gitlab/seeder.rb10
-rw-r--r--lib/gitlab/task_helpers.rb (renamed from lib/tasks/gitlab/task_helpers.rb)9
-rw-r--r--lib/gitlab/upgrader.rb3
-rw-r--r--lib/support/nginx/gitlab4
-rw-r--r--lib/support/nginx/gitlab-ssl4
-rw-r--r--lib/system_check/helpers.rb2
-rw-r--r--lib/tasks/flay.rake2
-rw-r--r--lib/tasks/gitlab/backup.rake36
-rw-r--r--lib/tasks/gitlab/check.rake24
-rw-r--r--lib/tasks/gitlab/cleanup.rake8
-rw-r--r--lib/tasks/gitlab/git.rake8
-rw-r--r--lib/tasks/gitlab/gitaly.rake8
-rw-r--r--lib/tasks/gitlab/helpers.rake4
-rw-r--r--lib/tasks/gitlab/info.rake2
-rw-r--r--lib/tasks/gitlab/setup.rake2
-rw-r--r--lib/tasks/gitlab/shell.rake8
-rw-r--r--lib/tasks/gitlab/workhorse.rake2
-rw-r--r--lib/tasks/haml-lint.rake9
-rw-r--r--lib/tasks/migrate/setup_postgresql.rake20
-rw-r--r--qa/qa.rb5
-rw-r--r--qa/qa/factory/dependency.rb7
-rw-r--r--qa/qa/factory/resource/merge_request.rb49
-rw-r--r--qa/qa/page/base.rb24
-rw-r--r--qa/qa/page/group/show.rb14
-rw-r--r--qa/qa/page/main/login.rb6
-rw-r--r--qa/qa/page/merge_request/new.rb31
-rw-r--r--qa/qa/page/project/new.rb4
-rw-r--r--qa/qa/page/project/settings/common.rb7
-rw-r--r--qa/qa/page/project/show.rb21
-rw-r--r--qa/qa/specs/features/merge_request/create_spec.rb17
-rw-r--r--qa/spec/factory/dependency_spec.rb13
-rwxr-xr-xscripts/static-analysis56
-rw-r--r--spec/features/commits_spec.rb2
-rw-r--r--spec/features/projects/blobs/edit_spec.rb22
-rw-r--r--spec/helpers/application_helper_spec.rb4
-rw-r--r--spec/javascripts/pipelines/pipelines_table_row_spec.js4
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js38
-rw-r--r--spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js208
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb10
-rw-r--r--spec/lib/gitlab/popen/runner_spec.rb139
-rw-r--r--spec/lib/gitlab/popen_spec.rb16
-rw-r--r--spec/models/ci/build_spec.rb2
-rw-r--r--spec/models/commit_spec.rb6
-rw-r--r--spec/models/merge_request_spec.rb32
-rw-r--r--spec/models/project_services/jira_service_spec.rb23
-rw-r--r--spec/models/repository_spec.rb30
-rw-r--r--spec/requests/api/commits_spec.rb12
-rw-r--r--spec/requests/api/merge_requests_spec.rb2
-rw-r--r--spec/requests/api/v3/commits_spec.rb6
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb25
-rw-r--r--spec/support/javascript_fixtures_helpers.rb1
-rw-r--r--spec/tasks/gitlab/gitaly_rake_spec.rb6
-rw-r--r--spec/tasks/gitlab/task_helpers_spec.rb1
103 files changed, 1298 insertions, 724 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c1d78ef2d48..be18520b876 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -321,6 +321,7 @@ setup-test-env:
expire_in: 7d
paths:
- tmp/tests
+ - config/secrets.yml
rspec-pg 0 27: *rspec-metadata-pg
rspec-pg 1 27: *rspec-metadata-pg
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 248c85304a9..5fc97c06f7c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,22 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 10.4.2 (2018-01-30)
+
+### Fixed (6 changes)
+
+- Fix copy/paste on iOS devices due to a bug in webkit. !15804
+- Fix missing "allow users to request access" option in public project permissions. !16485
+- Fix encoding issue when counting commit count. !16637
+- Fixes destination already exists, and some particular service errors on Import/Export error. !16714
+- Fix cache clear bug withg using : on Windows. !16740
+- Use has_table_privilege for TRIGGER on PostgreSQL.
+
+### Changed (1 change)
+
+- Vendor Auto DevOps template with DAST security checks enabled. !16691
+
+
## 10.4.1 (2018-01-24)
### Fixed (4 changes)
diff --git a/Gemfile b/Gemfile
index ee576c53fe9..05f72b6482f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -325,7 +325,7 @@ group :development, :test do
gem 'spinach-rerun-reporter', '~> 0.0.2'
gem 'rspec_profiling', '~> 0.0.5'
gem 'rspec-set', '~> 0.1.3'
- gem 'rspec-parameterized'
+ gem 'rspec-parameterized', require: false
# Prevent occasions where minitest is not bundled in packaged versions of ruby (see #3826)
gem 'minitest', '~> 5.7.0'
diff --git a/Gemfile.lock b/Gemfile.lock
index 5532888d179..1a3c8f42469 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -304,7 +304,7 @@ GEM
mime-types (>= 1.16)
posix-spawn (~> 0.3)
gitlab-markup (1.6.3)
- gitlab-styles (2.3.1)
+ gitlab-styles (2.3.2)
rubocop (~> 0.51)
rubocop-gitlab-security (~> 0.1.0)
rubocop-rspec (~> 1.19)
diff --git a/app/assets/javascripts/awards_handler.js b/app/assets/javascripts/awards_handler.js
index 622764107ad..d9341837149 100644
--- a/app/assets/javascripts/awards_handler.js
+++ b/app/assets/javascripts/awards_handler.js
@@ -1,8 +1,10 @@
/* eslint-disable class-methods-use-this */
import _ from 'underscore';
import Cookies from 'js-cookie';
+import { s__ } from './locale';
import { isInIssuePage, updateTooltipTitle } from './lib/utils/common_utils';
-import Flash from './flash';
+import flash from './flash';
+import axios from './lib/utils/axios_utils';
const animationEndEventString = 'animationend webkitAnimationEnd MSAnimationEnd oAnimationEnd';
const transitionEndEventString = 'transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd';
@@ -441,13 +443,15 @@ class AwardsHandler {
if (this.isUserAuthored($emojiButton)) {
this.userAuthored($emojiButton);
} else {
- $.post(awardUrl, {
+ axios.post(awardUrl, {
name: emoji,
- }, (data) => {
+ })
+ .then(({ data }) => {
if (data.ok) {
callback();
}
- }).fail(() => new Flash('Something went wrong on our end.'));
+ })
+ .catch(() => flash(s__('Something went wrong on our end.')));
}
}
diff --git a/app/assets/javascripts/blob_edit/edit_blob.js b/app/assets/javascripts/blob_edit/edit_blob.js
index b37988a674d..a25f7fb3dcd 100644
--- a/app/assets/javascripts/blob_edit/edit_blob.js
+++ b/app/assets/javascripts/blob_edit/edit_blob.js
@@ -1,5 +1,8 @@
/* global ace */
+import axios from '~/lib/utils/axios_utils';
+import createFlash from '~/flash';
+import { __ } from '~/locale';
import TemplateSelectorMediator from '../blob/file_template_mediator';
export default class EditBlob {
@@ -56,12 +59,14 @@ export default class EditBlob {
if (paneId === '#preview') {
this.$toggleButton.hide();
- return $.post(currentLink.data('preview-url'), {
+ axios.post(currentLink.data('preview-url'), {
content: this.editor.getValue(),
- }, (response) => {
- currentPane.empty().append(response);
- return currentPane.renderGFM();
- });
+ })
+ .then(({ data }) => {
+ currentPane.empty().append(data);
+ currentPane.renderGFM();
+ })
+ .catch(() => createFlash(__('An error occurred previewing the blob')));
}
this.$toggleButton.show();
diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js
index 144caf1d278..e2a008e8904 100644
--- a/app/assets/javascripts/compare.js
+++ b/app/assets/javascripts/compare.js
@@ -1,5 +1,6 @@
/* eslint-disable func-names, space-before-function-paren, wrap-iife, quotes, no-var, object-shorthand, consistent-return, no-unused-vars, comma-dangle, vars-on-top, prefer-template, max-len */
import { localTimeAgo } from './lib/utils/datetime_utility';
+import axios from './lib/utils/axios_utils';
export default class Compare {
constructor(opts) {
@@ -41,17 +42,14 @@ export default class Compare {
}
getTargetProject() {
- return $.ajax({
- url: this.opts.targetProjectUrl,
- data: {
- target_project_id: $("input[name='merge_request[target_project_id]']").val()
- },
- beforeSend: function() {
- return $('.mr_target_commit').empty();
+ $('.mr_target_commit').empty();
+
+ return axios.get(this.opts.targetProjectUrl, {
+ params: {
+ target_project_id: $("input[name='merge_request[target_project_id]']").val(),
},
- success: function(html) {
- return $('.js-target-branch-dropdown .dropdown-content').html(html);
- }
+ }).then(({ data }) => {
+ $('.js-target-branch-dropdown .dropdown-content').html(data);
});
}
@@ -68,22 +66,19 @@ export default class Compare {
});
}
- static sendAjax(url, loading, target, data) {
- var $target;
- $target = $(target);
- return $.ajax({
- url: url,
- data: data,
- beforeSend: function() {
- loading.show();
- return $target.empty();
- },
- success: function(html) {
- loading.hide();
- $target.html(html);
- var className = '.' + $target[0].className.replace(' ', '.');
- localTimeAgo($('.js-timeago', className));
- }
+ static sendAjax(url, loading, target, params) {
+ const $target = $(target);
+
+ loading.show();
+ $target.empty();
+
+ return axios.get(url, {
+ params,
+ }).then(({ data }) => {
+ loading.hide();
+ $target.html(data);
+ const className = '.' + $target[0].className.replace(' ', '.');
+ localTimeAgo($('.js-timeago', className));
});
}
}
diff --git a/app/assets/javascripts/compare_autocomplete.js b/app/assets/javascripts/compare_autocomplete.js
index e633ef8a29e..59899e97be1 100644
--- a/app/assets/javascripts/compare_autocomplete.js
+++ b/app/assets/javascripts/compare_autocomplete.js
@@ -1,4 +1,7 @@
/* eslint-disable func-names, space-before-function-paren, one-var, no-var, one-var-declaration-per-line, object-shorthand, comma-dangle, prefer-arrow-callback, no-else-return, newline-per-chained-call, wrap-iife, max-len */
+import { __ } from './locale';
+import axios from './lib/utils/axios_utils';
+import flash from './flash';
export default function initCompareAutocomplete() {
$('.js-compare-dropdown').each(function() {
@@ -10,15 +13,14 @@ export default function initCompareAutocomplete() {
const $filterInput = $('input[type="search"]', $dropdownContainer);
$dropdown.glDropdown({
data: function(term, callback) {
- return $.ajax({
- url: $dropdown.data('refs-url'),
- data: {
+ axios.get($dropdown.data('refsUrl'), {
+ params: {
ref: $dropdown.data('ref'),
search: term,
- }
- }).done(function(refs) {
- return callback(refs);
- });
+ },
+ }).then(({ data }) => {
+ callback(data);
+ }).catch(() => flash(__('Error fetching refs')));
},
selectable: true,
filterable: true,
diff --git a/app/assets/javascripts/create_merge_request_dropdown.js b/app/assets/javascripts/create_merge_request_dropdown.js
index bc23a72762f..482d83621e2 100644
--- a/app/assets/javascripts/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/create_merge_request_dropdown.js
@@ -1,5 +1,6 @@
/* eslint-disable no-new */
import _ from 'underscore';
+import axios from './lib/utils/axios_utils';
import Flash from './flash';
import DropLab from './droplab/drop_lab';
import ISetter from './droplab/plugins/input_setter';
@@ -74,60 +75,52 @@ export default class CreateMergeRequestDropdown {
}
checkAbilityToCreateBranch() {
- return $.ajax({
- type: 'GET',
- dataType: 'json',
- url: this.canCreatePath,
- beforeSend: () => this.setUnavailableButtonState(),
- })
- .done((data) => {
- this.setUnavailableButtonState(false);
-
- if (data.can_create_branch) {
- this.available();
- this.enable();
-
- if (!this.droplabInitialized) {
- this.droplabInitialized = true;
- this.initDroplab();
- this.bindEvents();
+ this.setUnavailableButtonState();
+
+ axios.get(this.canCreatePath)
+ .then(({ data }) => {
+ this.setUnavailableButtonState(false);
+
+ if (data.can_create_branch) {
+ this.available();
+ this.enable();
+
+ if (!this.droplabInitialized) {
+ this.droplabInitialized = true;
+ this.initDroplab();
+ this.bindEvents();
+ }
+ } else if (data.has_related_branch) {
+ this.hide();
}
- } else if (data.has_related_branch) {
- this.hide();
- }
- }).fail(() => {
- this.unavailable();
- this.disable();
- new Flash('Failed to check if a new branch can be created.');
- });
+ })
+ .catch(() => {
+ this.unavailable();
+ this.disable();
+ Flash('Failed to check if a new branch can be created.');
+ });
}
createBranch() {
- return $.ajax({
- method: 'POST',
- dataType: 'json',
- url: this.createBranchPath,
- beforeSend: () => (this.isCreatingBranch = true),
- })
- .done((data) => {
- this.branchCreated = true;
- window.location.href = data.url;
- })
- .fail(() => new Flash('Failed to create a branch for this issue. Please try again.'));
+ this.isCreatingBranch = true;
+
+ return axios.post(this.createBranchPath)
+ .then(({ data }) => {
+ this.branchCreated = true;
+ window.location.href = data.url;
+ })
+ .catch(() => Flash('Failed to create a branch for this issue. Please try again.'));
}
createMergeRequest() {
- return $.ajax({
- method: 'POST',
- dataType: 'json',
- url: this.createMrPath,
- beforeSend: () => (this.isCreatingMergeRequest = true),
- })
- .done((data) => {
- this.mergeRequestCreated = true;
- window.location.href = data.url;
- })
- .fail(() => new Flash('Failed to create Merge Request. Please try again.'));
+ this.isCreatingMergeRequest = true;
+
+ return axios.post(this.createMrPath)
+ .then(({ data }) => {
+ this.mergeRequestCreated = true;
+ window.location.href = data.url;
+ })
+ .catch(() => Flash('Failed to create Merge Request. Please try again.'));
}
disable() {
@@ -200,39 +193,33 @@ export default class CreateMergeRequestDropdown {
getRef(ref, target = 'all') {
if (!ref) return false;
- return $.ajax({
- method: 'GET',
- dataType: 'json',
- url: this.refsPath + ref,
- beforeSend: () => {
- this.isGettingRef = true;
- },
- })
- .always(() => {
- this.isGettingRef = false;
- })
- .done((data) => {
- const branches = data[Object.keys(data)[0]];
- const tags = data[Object.keys(data)[1]];
- let result;
+ return axios.get(this.refsPath + ref)
+ .then(({ data }) => {
+ const branches = data[Object.keys(data)[0]];
+ const tags = data[Object.keys(data)[1]];
+ let result;
+
+ if (target === 'branch') {
+ result = CreateMergeRequestDropdown.findByValue(branches, ref);
+ } else {
+ result = CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
+ CreateMergeRequestDropdown.findByValue(tags, ref, true);
+ this.suggestedRef = result;
+ }
- if (target === 'branch') {
- result = CreateMergeRequestDropdown.findByValue(branches, ref);
- } else {
- result = CreateMergeRequestDropdown.findByValue(branches, ref, true) ||
- CreateMergeRequestDropdown.findByValue(tags, ref, true);
- this.suggestedRef = result;
- }
+ this.isGettingRef = false;
- return this.updateInputState(target, ref, result);
- })
- .fail(() => {
- this.unavailable();
- this.disable();
- new Flash('Failed to get ref.');
+ return this.updateInputState(target, ref, result);
+ })
+ .catch(() => {
+ this.unavailable();
+ this.disable();
+ new Flash('Failed to get ref.');
- return false;
- });
+ this.isGettingRef = false;
+
+ return false;
+ });
}
getTargetData(target) {
@@ -332,12 +319,12 @@ export default class CreateMergeRequestDropdown {
xhr = this.createBranch();
}
- xhr.fail(() => {
+ xhr.catch(() => {
this.isCreatingMergeRequest = false;
this.isCreatingBranch = false;
- });
- xhr.always(() => this.enable());
+ this.enable();
+ });
this.disable();
}
diff --git a/app/assets/javascripts/dropzone_input.js b/app/assets/javascripts/dropzone_input.js
index 550dbdda922..ba89e5726fa 100644
--- a/app/assets/javascripts/dropzone_input.js
+++ b/app/assets/javascripts/dropzone_input.js
@@ -2,6 +2,7 @@ import Dropzone from 'dropzone';
import _ from 'underscore';
import './preview_markdown';
import csrf from './lib/utils/csrf';
+import axios from './lib/utils/axios_utils';
Dropzone.autoDiscover = false;
@@ -235,25 +236,21 @@ export default function dropzoneInput(form) {
uploadFile = (item, filename) => {
const formData = new FormData();
formData.append('file', item, filename);
- return $.ajax({
- url: uploadsPath,
- type: 'POST',
- data: formData,
- dataType: 'json',
- processData: false,
- contentType: false,
- headers: csrf.headers,
- beforeSend: () => {
- showSpinner();
- return closeAlertMessage();
- },
- success: (e, text, response) => {
- const md = response.responseJSON.link.markdown;
+
+ showSpinner();
+ closeAlertMessage();
+
+ axios.post(uploadsPath, formData)
+ .then(({ data }) => {
+ const md = data.link.markdown;
+
insertToTextArea(filename, md);
- },
- error: response => showError(response.responseJSON.message),
- complete: () => closeSpinner(),
- });
+ closeSpinner();
+ })
+ .catch((e) => {
+ showError(e.response.data.message);
+ closeSpinner();
+ });
};
updateAttachingMessage = (files, messageContainer) => {
diff --git a/app/assets/javascripts/due_date_select.js b/app/assets/javascripts/due_date_select.js
index ada985913bb..bd4c58b7cb1 100644
--- a/app/assets/javascripts/due_date_select.js
+++ b/app/assets/javascripts/due_date_select.js
@@ -1,6 +1,7 @@
/* global dateFormat */
import Pikaday from 'pikaday';
+import axios from './lib/utils/axios_utils';
import { parsePikadayDate, pikadayToString } from './lib/utils/datefix';
class DueDateSelect {
@@ -125,37 +126,30 @@ class DueDateSelect {
}
submitSelectedDate(isDropdown) {
- return $.ajax({
- type: 'PUT',
- url: this.issueUpdateURL,
- data: this.datePayload,
- dataType: 'json',
- beforeSend: () => {
- const selectedDateValue = this.datePayload[this.abilityName].due_date;
- const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value';
+ const selectedDateValue = this.datePayload[this.abilityName].due_date;
+ const displayedDateStyle = this.displayedDate !== 'No due date' ? 'bold' : 'no-value';
- this.$loading.removeClass('hidden').fadeIn();
+ this.$loading.removeClass('hidden').fadeIn();
- if (isDropdown) {
- this.$dropdown.trigger('loading.gl.dropdown');
- this.$selectbox.hide();
- }
+ if (isDropdown) {
+ this.$dropdown.trigger('loading.gl.dropdown');
+ this.$selectbox.hide();
+ }
- this.$value.css('display', '');
- this.$valueContent.html(`<span class='${displayedDateStyle}'>${this.displayedDate}</span>`);
- this.$sidebarValue.html(this.displayedDate);
+ this.$value.css('display', '');
+ this.$valueContent.html(`<span class='${displayedDateStyle}'>${this.displayedDate}</span>`);
+ this.$sidebarValue.html(this.displayedDate);
- return selectedDateValue.length ?
- $('.js-remove-due-date-holder').removeClass('hidden') :
- $('.js-remove-due-date-holder').addClass('hidden');
- },
- }).done(() => {
- if (isDropdown) {
- this.$dropdown.trigger('loaded.gl.dropdown');
- this.$dropdown.dropdown('toggle');
- }
- return this.$loading.fadeOut();
- });
+ $('.js-remove-due-date-holder').toggleClass('hidden', selectedDateValue.length);
+
+ return axios.put(this.issueUpdateURL, this.datePayload)
+ .then(() => {
+ if (isDropdown) {
+ this.$dropdown.trigger('loaded.gl.dropdown');
+ this.$dropdown.dropdown('toggle');
+ }
+ return this.$loading.fadeOut();
+ });
}
}
diff --git a/app/assets/javascripts/filterable_list.js b/app/assets/javascripts/filterable_list.js
index 9e91f72b2ea..a10f027de53 100644
--- a/app/assets/javascripts/filterable_list.js
+++ b/app/assets/javascripts/filterable_list.js
@@ -1,4 +1,5 @@
import _ from 'underscore';
+import axios from './lib/utils/axios_utils';
/**
* Makes search request for content when user types a value in the search input.
@@ -54,32 +55,26 @@ export default class FilterableList {
this.listFilterElement.removeEventListener('input', this.debounceFilter);
}
- filterResults(queryData) {
+ filterResults(params) {
if (this.isBusy) {
return false;
}
$(this.listHolderElement).fadeTo(250, 0.5);
- return $.ajax({
- url: this.getFilterEndpoint(),
- data: queryData,
- type: 'GET',
- dataType: 'json',
- context: this,
- complete: this.onFilterComplete,
- beforeSend: () => {
- this.isBusy = true;
- },
- success: (response, textStatus, xhr) => {
- this.onFilterSuccess(response, xhr, queryData);
- },
- });
+ this.isBusy = true;
+
+ return axios.get(this.getFilterEndpoint(), {
+ params,
+ }).then((res) => {
+ this.onFilterSuccess(res, params);
+ this.onFilterComplete();
+ }).catch(() => this.onFilterComplete());
}
- onFilterSuccess(response, xhr, queryData) {
- if (response.html) {
- this.listHolderElement.innerHTML = response.html;
+ onFilterSuccess(response, queryData) {
+ if (response.data.html) {
+ this.listHolderElement.innerHTML = response.data.html;
}
// Change url so if user reload a page - search results are saved
diff --git a/app/assets/javascripts/groups/groups_filterable_list.js b/app/assets/javascripts/groups/groups_filterable_list.js
index 2db233b09da..31d56d15c23 100644
--- a/app/assets/javascripts/groups/groups_filterable_list.js
+++ b/app/assets/javascripts/groups/groups_filterable_list.js
@@ -1,6 +1,6 @@
import FilterableList from '~/filterable_list';
import eventHub from './event_hub';
-import { getParameterByName } from '../lib/utils/common_utils';
+import { normalizeHeaders, getParameterByName } from '../lib/utils/common_utils';
export default class GroupFilterableList extends FilterableList {
constructor({ form, filter, holder, filterEndpoint, pagePath, dropdownSel, filterInputField }) {
@@ -94,23 +94,14 @@ export default class GroupFilterableList extends FilterableList {
this.form.querySelector(`[name="${this.filterInputField}"]`).value = '';
}
- onFilterSuccess(data, xhr, queryData) {
+ onFilterSuccess(res, queryData) {
const currentPath = this.getPagePath(queryData);
- const paginationData = {
- 'X-Per-Page': xhr.getResponseHeader('X-Per-Page'),
- 'X-Page': xhr.getResponseHeader('X-Page'),
- 'X-Total': xhr.getResponseHeader('X-Total'),
- 'X-Total-Pages': xhr.getResponseHeader('X-Total-Pages'),
- 'X-Next-Page': xhr.getResponseHeader('X-Next-Page'),
- 'X-Prev-Page': xhr.getResponseHeader('X-Prev-Page'),
- };
-
window.history.replaceState({
page: currentPath,
}, document.title, currentPath);
- eventHub.$emit('updateGroups', data, Object.prototype.hasOwnProperty.call(queryData, this.filterInputField));
- eventHub.$emit('updatePagination', paginationData);
+ eventHub.$emit('updateGroups', res.data, Object.prototype.hasOwnProperty.call(queryData, this.filterInputField));
+ eventHub.$emit('updatePagination', normalizeHeaders(res.headers));
}
}
diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue
index 025e38ea99a..5afae93724b 100644
--- a/app/assets/javascripts/monitoring/components/dashboard.vue
+++ b/app/assets/javascripts/monitoring/components/dashboard.vue
@@ -76,7 +76,13 @@
.then(data => this.store.storeDeploymentData(data))
.catch(() => new Flash('Error getting deployment information.')),
])
- .then(() => { this.showEmptyState = false; })
+ .then(() => {
+ if (this.store.groups.length < 1) {
+ this.state = 'noData';
+ return;
+ }
+ this.showEmptyState = false;
+ })
.catch(() => { this.state = 'unableToConnect'; });
},
diff --git a/app/assets/javascripts/monitoring/components/empty_state.vue b/app/assets/javascripts/monitoring/components/empty_state.vue
index 87d1975d5ad..56cd60c583b 100644
--- a/app/assets/javascripts/monitoring/components/empty_state.vue
+++ b/app/assets/javascripts/monitoring/components/empty_state.vue
@@ -34,16 +34,23 @@
svgUrl: this.emptyGettingStartedSvgPath,
title: 'Get started with performance monitoring',
description: `Stay updated about the performance and health
-of your environment by configuring Prometheus to monitor your deployments.`,
+ of your environment by configuring Prometheus to monitor your deployments.`,
buttonText: 'Configure Prometheus',
},
loading: {
svgUrl: this.emptyLoadingSvgPath,
title: 'Waiting for performance data',
description: `Creating graphs uses the data from the Prometheus server.
-If this takes a long time, ensure that data is available.`,
+ If this takes a long time, ensure that data is available.`,
buttonText: 'View documentation',
},
+ noData: {
+ svgUrl: this.emptyUnableToConnectSvgPath,
+ title: 'No data found',
+ description: `You are connected to the Prometheus server, but there is currently
+ no data to display.`,
+ buttonText: 'Configure Prometheus',
+ },
unableToConnect: {
svgUrl: this.emptyUnableToConnectSvgPath,
title: 'Unable to connect to Prometheus server',
diff --git a/app/assets/javascripts/sidebar/components/assignees/assignees.js b/app/assets/javascripts/sidebar/components/assignees/assignees.js
index 7e5feac622c..643877b9d47 100644
--- a/app/assets/javascripts/sidebar/components/assignees/assignees.js
+++ b/app/assets/javascripts/sidebar/components/assignees/assignees.js
@@ -84,7 +84,7 @@ export default {
return !this.showLess || (index < this.defaultRenderCount && this.showLess);
},
avatarUrl(user) {
- return user.avatar || user.avatar_url;
+ return user.avatar || user.avatar_url || gon.default_avatar_url;
},
assigneeUrl(user) {
return `${this.rootPath}${user.username}`;
diff --git a/app/assets/javascripts/users_select.js b/app/assets/javascripts/users_select.js
index f249bd036d6..ab108906732 100644
--- a/app/assets/javascripts/users_select.js
+++ b/app/assets/javascripts/users_select.js
@@ -492,7 +492,7 @@ function UsersSelect(currentUser, els, options = {}) {
renderRow: function(user) {
var avatar, img, listClosingTags, listWithName, listWithUserName, username;
username = user.username ? "@" + user.username : "";
- avatar = user.avatar_url ? user.avatar_url : false;
+ avatar = user.avatar_url ? user.avatar_url : gon.default_avatar_url;
let selected = false;
@@ -513,9 +513,7 @@ function UsersSelect(currentUser, els, options = {}) {
if (user.beforeDivider != null) {
`<li><a href='#' class='${selected === true ? 'is-active' : ''}'>${_.escape(user.name)}</a></li>`;
} else {
- if (avatar) {
- img = "<img src='" + avatar + "' class='avatar avatar-inline' width='32' />";
- }
+ img = "<img src='" + avatar + "' class='avatar avatar-inline' width='32' />";
}
return `
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js
index 85bfd03a3cf..de6e5149a87 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_header.js
@@ -76,6 +76,7 @@ export default {
<a
href="#modal_merge_info"
data-toggle="modal"
+ :disabled="mr.sourceBranchRemoved"
class="btn btn-sm inline">
Check out branch
</a>
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
deleted file mode 100644
index 7f8d78cab73..00000000000
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.js
+++ /dev/null
@@ -1,139 +0,0 @@
-import Flash from '../../../flash';
-import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
-import tooltip from '../../../vue_shared/directives/tooltip';
-import loadingIcon from '../../../vue_shared/components/loading_icon.vue';
-import statusIcon from '../mr_widget_status_icon.vue';
-import eventHub from '../../event_hub';
-
-export default {
- name: 'MRWidgetMerged',
- props: {
- mr: { type: Object, required: true },
- service: { type: Object, required: true },
- },
- data() {
- return {
- isMakingRequest: false,
- };
- },
- directives: {
- tooltip,
- },
- components: {
- 'mr-widget-author-and-time': mrWidgetAuthorTime,
- loadingIcon,
- statusIcon,
- },
- computed: {
- shouldShowRemoveSourceBranch() {
- const { sourceBranchRemoved, isRemovingSourceBranch, canRemoveSourceBranch } = this.mr;
-
- return !sourceBranchRemoved && canRemoveSourceBranch &&
- !this.isMakingRequest && !isRemovingSourceBranch;
- },
- shouldShowSourceBranchRemoving() {
- const { sourceBranchRemoved, isRemovingSourceBranch } = this.mr;
- return !sourceBranchRemoved && (isRemovingSourceBranch || this.isMakingRequest);
- },
- shouldShowMergedButtons() {
- const { canRevertInCurrentMR, canCherryPickInCurrentMR, revertInForkPath,
- cherryPickInForkPath } = this.mr;
-
- return canRevertInCurrentMR || canCherryPickInCurrentMR ||
- revertInForkPath || cherryPickInForkPath;
- },
- },
- methods: {
- removeSourceBranch() {
- this.isMakingRequest = true;
- this.service.removeSourceBranch()
- .then(res => res.data)
- .then((data) => {
- if (data.message === 'Branch was removed') {
- eventHub.$emit('MRWidgetUpdateRequested', () => {
- this.isMakingRequest = false;
- });
- }
- })
- .catch(() => {
- this.isMakingRequest = false;
- new Flash('Something went wrong. Please try again.'); // eslint-disable-line
- });
- },
- },
- template: `
- <div class="mr-widget-body media">
- <status-icon status="success" />
- <div class="media-body">
- <div class="space-children">
- <mr-widget-author-and-time
- actionText="Merged by"
- :author="mr.metrics.mergedBy"
- :date-title="mr.metrics.mergedAt"
- :date-readable="mr.metrics.readableMergedAt" />
- <a
- v-if="mr.canRevertInCurrentMR"
- v-tooltip
- class="btn btn-close btn-xs"
- href="#modal-revert-commit"
- data-toggle="modal"
- data-container="body"
- title="Revert this merge request in a new merge request">
- Revert
- </a>
- <a
- v-else-if="mr.revertInForkPath"
- v-tooltip
- class="btn btn-close btn-xs"
- data-method="post"
- :href="mr.revertInForkPath"
- title="Revert this merge request in a new merge request">
- Revert
- </a>
- <a
- v-if="mr.canCherryPickInCurrentMR"
- v-tooltip
- class="btn btn-default btn-xs"
- href="#modal-cherry-pick-commit"
- data-toggle="modal"
- data-container="body"
- title="Cherry-pick this merge request in a new merge request">
- Cherry-pick
- </a>
- <a
- v-else-if="mr.cherryPickInForkPath"
- v-tooltip
- class="btn btn-default btn-xs"
- data-method="post"
- :href="mr.cherryPickInForkPath"
- title="Cherry-pick this merge request in a new merge request">
- Cherry-pick
- </a>
- </div>
- <section class="mr-info-list">
- <p>
- The changes were merged into
- <span class="label-branch">
- <a :href="mr.targetBranchPath">{{mr.targetBranch}}</a>
- </span>
- </p>
- <p v-if="mr.sourceBranchRemoved">The source branch has been removed</p>
- <p v-if="shouldShowRemoveSourceBranch" class="space-children">
- <span>You can remove source branch now</span>
- <button
- @click="removeSourceBranch"
- :disabled="isMakingRequest"
- type="button"
- class="btn btn-xs btn-default js-remove-branch-button">
- Remove Source Branch
- </button>
- </p>
- <p v-if="shouldShowSourceBranchRemoving">
- <loading-icon inline />
- <span>The source branch is being removed</span>
- </p>
- </section>
- </div>
- </div>
- `,
-};
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
new file mode 100644
index 00000000000..a92e0b3c124
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_merged.vue
@@ -0,0 +1,192 @@
+<script>
+ import Flash from '~/flash';
+ import tooltip from '~/vue_shared/directives/tooltip';
+ import loadingIcon from '~/vue_shared/components/loading_icon.vue';
+ import { s__, __ } from '~/locale';
+ import mrWidgetAuthorTime from '../../components/mr_widget_author_time';
+ import statusIcon from '../mr_widget_status_icon.vue';
+ import eventHub from '../../event_hub';
+
+ export default {
+ name: 'MRWidgetMerged',
+ directives: {
+ tooltip,
+ },
+ components: {
+ mrWidgetAuthorTime,
+ loadingIcon,
+ statusIcon,
+ },
+ props: {
+ mr: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ service: {
+ type: Object,
+ required: true,
+ default: () => ({}),
+ },
+ },
+ data() {
+ return {
+ isMakingRequest: false,
+ };
+ },
+ computed: {
+ shouldShowRemoveSourceBranch() {
+ const {
+ sourceBranchRemoved,
+ isRemovingSourceBranch,
+ canRemoveSourceBranch,
+ } = this.mr;
+
+ return !sourceBranchRemoved &&
+ canRemoveSourceBranch &&
+ !this.isMakingRequest &&
+ !isRemovingSourceBranch;
+ },
+ shouldShowSourceBranchRemoving() {
+ const {
+ sourceBranchRemoved,
+ isRemovingSourceBranch,
+ } = this.mr;
+ return !sourceBranchRemoved &&
+ (isRemovingSourceBranch || this.isMakingRequest);
+ },
+ shouldShowMergedButtons() {
+ const {
+ canRevertInCurrentMR,
+ canCherryPickInCurrentMR,
+ revertInForkPath,
+ cherryPickInForkPath,
+ } = this.mr;
+
+ return canRevertInCurrentMR ||
+ canCherryPickInCurrentMR ||
+ revertInForkPath ||
+ cherryPickInForkPath;
+ },
+ revertTitle() {
+ return s__('mrWidget|Revert this merge request in a new merge request');
+ },
+ cherryPickTitle() {
+ return s__('mrWidget|Cherry-pick this merge request in a new merge request');
+ },
+ revertLabel() {
+ return s__('mrWidget|Revert');
+ },
+ cherryPickLabel() {
+ return s__('mrWidget|Cherry-pick');
+ },
+ },
+ methods: {
+ removeSourceBranch() {
+ this.isMakingRequest = true;
+
+ this.service.removeSourceBranch()
+ .then(res => res.data)
+ .then((data) => {
+ if (data.message === 'Branch was removed') {
+ eventHub.$emit('MRWidgetUpdateRequested', () => {
+ this.isMakingRequest = false;
+ });
+ }
+ })
+ .catch(() => {
+ this.isMakingRequest = false;
+ Flash(__('Something went wrong. Please try again.'));
+ });
+ },
+ },
+ };
+</script>
+<template>
+ <div class="mr-widget-body media">
+ <status-icon status="success" />
+ <div class="media-body">
+ <div class="space-children">
+ <mr-widget-author-time
+ :action-text="s__('mrWidget|Merged by')"
+ :author="mr.metrics.mergedBy"
+ :date-title="mr.metrics.mergedAt"
+ :date-readable="mr.metrics.readableMergedAt"
+ />
+ <a
+ v-if="mr.canRevertInCurrentMR"
+ v-tooltip
+ class="btn btn-close btn-xs"
+ href="#modal-revert-commit"
+ data-toggle="modal"
+ data-container="body"
+ :title="revertTitle"
+ >
+ {{ revertLabel }}
+ </a>
+ <a
+ v-else-if="mr.revertInForkPath"
+ v-tooltip
+ class="btn btn-close btn-xs"
+ data-method="post"
+ :href="mr.revertInForkPath"
+ :title="revertTitle"
+ >
+ {{ revertLabel }}
+ </a>
+ <a
+ v-if="mr.canCherryPickInCurrentMR"
+ v-tooltip
+ class="btn btn-default btn-xs"
+ href="#modal-cherry-pick-commit"
+ data-toggle="modal"
+ data-container="body"
+ :title="cherryPickTitle"
+ >
+ {{ cherryPickLabel }}
+ </a>
+ <a
+ v-else-if="mr.cherryPickInForkPath"
+ v-tooltip
+ class="btn btn-default btn-xs"
+ data-method="post"
+ :href="mr.cherryPickInForkPath"
+ :title="cherryPickTitle"
+ >
+ {{ cherryPickLabel }}
+ </a>
+ </div>
+ <section class="mr-info-list">
+ <p>
+ {{ s__("mrWidget|The changes were merged into") }}
+ <span class="label-branch">
+ <a :href="mr.targetBranchPath">{{ mr.targetBranch }}</a>
+ </span>
+ </p>
+ <p v-if="mr.sourceBranchRemoved">
+ {{ s__("mrWidget|The source branch has been removed") }}
+ </p>
+ <p
+ v-if="shouldShowRemoveSourceBranch"
+ class="space-children"
+ >
+ <span>{{ s__("mrWidget|You can remove source branch now") }}</span>
+ <button
+ @click="removeSourceBranch"
+ :disabled="isMakingRequest"
+ type="button"
+ class="btn btn-xs btn-default js-remove-branch-button"
+ >
+ {{ s__("mrWidget|Remove Source Branch") }}
+ </button>
+ </p>
+ <p v-if="shouldShowSourceBranchRemoving">
+ <loading-icon :inline="true" />
+ <span>
+ {{ s__("mrWidget|The source branch is being removed") }}
+ </span>
+ </p>
+ </section>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_merge_request_widget/dependencies.js b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
index b930aca6877..2917090e073 100644
--- a/app/assets/javascripts/vue_merge_request_widget/dependencies.js
+++ b/app/assets/javascripts/vue_merge_request_widget/dependencies.js
@@ -16,7 +16,7 @@ export { default as WidgetMergeHelp } from './components/mr_widget_merge_help';
export { default as WidgetPipeline } from './components/mr_widget_pipeline.vue';
export { default as WidgetDeployment } from './components/mr_widget_deployment';
export { default as WidgetRelatedLinks } from './components/mr_widget_related_links';
-export { default as MergedState } from './components/states/mr_widget_merged';
+export { default as MergedState } from './components/states/mr_widget_merged.vue';
export { default as FailedToMerge } from './components/states/mr_widget_failed_to_merge.vue';
export { default as ClosedState } from './components/states/mr_widget_closed.vue';
export { default as MergingState } from './components/states/mr_widget_merging.vue';
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index d13407a06c8..6530327698b 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -89,7 +89,7 @@ module ApplicationHelper
end
def default_avatar
- 'no_avatar.png'
+ asset_path('no_avatar.png')
end
def last_commit(project)
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index df67fb243ad..6ced5fb0e24 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -466,7 +466,7 @@ module Ci
if cache && project.jobs_cache_index
cache = cache.merge(
- key: "#{cache[:key]}:#{project.jobs_cache_index}")
+ key: "#{cache[:key]}_#{project.jobs_cache_index}")
end
[cache]
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 4accb08eaf9..f6d4843abc3 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -989,13 +989,13 @@ class MergeRequest < ActiveRecord::Base
merged_at = metrics&.merged_at
notes_association = notes_with_associations
- # It is not guaranteed that Note#created_at will be strictly later than
- # MergeRequestMetric#merged_at. Nanoseconds on MySQL may break this
- # comparison, as will a HA environment if clocks are not *precisely*
- # synchronized. Add a minute's leeway to compensate for both possibilities
- cutoff = merged_at - 1.minute
-
if merged_at
+ # It is not guaranteed that Note#created_at will be strictly later than
+ # MergeRequestMetric#merged_at. Nanoseconds on MySQL may break this
+ # comparison, as will a HA environment if clocks are not *precisely*
+ # synchronized. Add a minute's leeway to compensate for both possibilities
+ cutoff = merged_at - 1.minute
+
notes_association = notes_association.where('created_at >= ?', cutoff)
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index 2be35b6ea9d..23147d7f666 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -43,7 +43,7 @@ class JiraService < IssueTrackerService
username: self.username,
password: self.password,
site: URI.join(url, '/').to_s,
- context_path: url.path,
+ context_path: url.path.chomp('/'),
auth_type: :basic,
read_timeout: 120,
use_cookies: true,
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 9f05535d4d4..262622f8bd0 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -9,7 +9,8 @@ module MergeRequests
Gitlab::GitalyClient.allow_n_plus_1_calls(&method(:find_new_commits))
# Be sure to close outstanding MRs before reloading them to avoid generating an
# empty diff during a manual merge
- close_merge_requests
+ close_upon_missing_source_branch_ref
+ post_merge_manually_merged
reload_merge_requests
reset_merge_when_pipeline_succeeds
mark_pending_todos_done
@@ -29,11 +30,22 @@ module MergeRequests
private
+ def close_upon_missing_source_branch_ref
+ # MergeRequest#reload_diff ignores not opened MRs. This means it won't
+ # create an `empty` diff for `closed` MRs without a source branch, keeping
+ # the latest diff state as the last _valid_ one.
+ merge_requests_for_source_branch.reject(&:source_branch_exists?).each do |mr|
+ MergeRequests::CloseService
+ .new(mr.target_project, @current_user)
+ .execute(mr)
+ end
+ end
+
# Collect open merge requests that target same branch we push into
# and close if push to master include last commit from merge request
# We need this to close(as merged) merge requests that were merged into
# target branch manually
- def close_merge_requests
+ def post_merge_manually_merged
commit_ids = @commits.map(&:id)
merge_requests = @project.merge_requests.preload(:latest_merge_request_diff).opened.where(target_branch: @branch_name).to_a
merge_requests = merge_requests.select(&:diff_head_commit)
diff --git a/app/views/projects/_last_push.html.haml b/app/views/projects/_last_push.html.haml
index 56eecece54c..6f5eb828902 100644
--- a/app/views/projects/_last_push.html.haml
+++ b/app/views/projects/_last_push.html.haml
@@ -14,5 +14,5 @@
#{time_ago_with_tooltip(event.created_at)}
.pull-right
- = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm" do
+ = link_to new_mr_path_from_push_event(event), title: _("New merge request"), class: "btn btn-info btn-sm qa-create-merge-request" do
#{ _('Create merge request') }
diff --git a/app/views/shared/form_elements/_description.html.haml b/app/views/shared/form_elements/_description.html.haml
index f65bb6a29e6..38e9899ca4b 100644
--- a/app/views/shared/form_elements/_description.html.haml
+++ b/app/views/shared/form_elements/_description.html.haml
@@ -15,7 +15,7 @@
= render layout: 'projects/md_preview', locals: { url: preview_url, referenced_users: true } do
= render 'projects/zen', f: form, attr: :description,
- classes: 'note-textarea',
+ classes: 'note-textarea qa-issuable-form-description',
placeholder: "Write a comment or drag your files here...",
supports_quick_actions: supports_quick_actions
= render 'shared/notes/hints', supports_quick_actions: supports_quick_actions
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index bb02dfa0d3a..79021a08719 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -65,7 +65,7 @@
%span.append-right-10
- if issuable.new_record?
- = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create'
+ = form.submit "Submit #{issuable.class.model_name.human.downcase}", class: 'btn btn-create qa-issuable-create-button'
- else
= form.submit 'Save changes', class: 'btn btn-save'
diff --git a/app/views/shared/issuable/form/_title.html.haml b/app/views/shared/issuable/form/_title.html.haml
index 64826d41d60..e81639f35ea 100644
--- a/app/views/shared/issuable/form/_title.html.haml
+++ b/app/views/shared/issuable/form/_title.html.haml
@@ -6,7 +6,7 @@
%div{ class: div_class }
= form.text_field :title, required: true, maxlength: 255, autofocus: true,
- autocomplete: 'off', class: 'form-control pad'
+ autocomplete: 'off', class: 'form-control pad qa-issuable-form-title'
- if issuable.respond_to?(:work_in_progress?)
%p.help-block
diff --git a/bin/upgrade.rb b/bin/upgrade.rb
deleted file mode 100755
index a5caecf8526..00000000000
--- a/bin/upgrade.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-require_relative "../lib/gitlab/upgrader"
-
-Gitlab::Upgrader.new.execute
diff --git a/changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml b/changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml
deleted file mode 100644
index f4c44983736..00000000000
--- a/changelogs/unreleased/32546-cannot-copy-paste-on-ios.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix copy/paste on iOS devices due to a bug in webkit
-merge_request: 15804
-author:
-type: fixed
diff --git a/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml b/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml
new file mode 100644
index 00000000000..0791847b64d
--- /dev/null
+++ b/changelogs/unreleased/34733-fix-default-avatar-when-gravatar-disabled.yml
@@ -0,0 +1,5 @@
+---
+title: Fix default avatar icon missing when Gravatar is disabled
+merge_request: 16681
+author: Felix Geyer
+type: fixed
diff --git a/changelogs/unreleased/42022-allow-users-to-request-access-not-visible-when-project-visibility-is-public.yml b/changelogs/unreleased/42022-allow-users-to-request-access-not-visible-when-project-visibility-is-public.yml
deleted file mode 100644
index 38684cd3c44..00000000000
--- a/changelogs/unreleased/42022-allow-users-to-request-access-not-visible-when-project-visibility-is-public.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix missing "allow users to request access" option in public project permissions
-merge_request: 16485
-author:
-type: fixed
diff --git a/changelogs/unreleased/42161-gitaly-commitservice-encoding-undefinedconversionerror-u-c124-from-utf-8-to-ascii-8bit.yml b/changelogs/unreleased/42161-gitaly-commitservice-encoding-undefinedconversionerror-u-c124-from-utf-8-to-ascii-8bit.yml
deleted file mode 100644
index c64bee9126e..00000000000
--- a/changelogs/unreleased/42161-gitaly-commitservice-encoding-undefinedconversionerror-u-c124-from-utf-8-to-ascii-8bit.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix encoding issue when counting commit count
-merge_request: 16637
-author:
-type: fixed
diff --git a/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml b/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml
new file mode 100644
index 00000000000..bd7e0d3a1b0
--- /dev/null
+++ b/changelogs/unreleased/42255-disable-mr-checkout-button-when-source-branch-deleted.yml
@@ -0,0 +1,5 @@
+---
+title: Disable MR check out button when source branch is deleted
+merge_request: 16631
+author: Jacopo Beschi @jacopo-beschi
+type: fixed
diff --git a/changelogs/unreleased/42327-import-from-gitlab-com-fails-destination-already-exists-and-is-not-an-empty-directory-error.yml b/changelogs/unreleased/42327-import-from-gitlab-com-fails-destination-already-exists-and-is-not-an-empty-directory-error.yml
deleted file mode 100644
index 660f4f5d42c..00000000000
--- a/changelogs/unreleased/42327-import-from-gitlab-com-fails-destination-already-exists-and-is-not-an-empty-directory-error.yml
+++ /dev/null
@@ -1,6 +0,0 @@
----
-title: Fixes destination already exists, and some particular service errors on Import/Export
- error
-merge_request: 16714
-author:
-type: fixed
diff --git a/changelogs/unreleased/fix-postgresql-table-grant.yml b/changelogs/unreleased/fix-postgresql-table-grant.yml
deleted file mode 100644
index 1c6559f6f73..00000000000
--- a/changelogs/unreleased/fix-postgresql-table-grant.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Use has_table_privilege for TRIGGER on PostgreSQL
-merge_request:
-author:
-type: fixed
diff --git a/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml b/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml
new file mode 100644
index 00000000000..1cffb213f23
--- /dev/null
+++ b/changelogs/unreleased/osw-fix-lost-diffs-when-source-branch-deleted.yml
@@ -0,0 +1,5 @@
+---
+title: Close and do not reload MR diffs when source branch is deleted
+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/changelogs/unreleased/sh-fix-jira-trailing-slash.yml b/changelogs/unreleased/sh-fix-jira-trailing-slash.yml
new file mode 100644
index 00000000000..786f6cd3727
--- /dev/null
+++ b/changelogs/unreleased/sh-fix-jira-trailing-slash.yml
@@ -0,0 +1,5 @@
+---
+title: Fix JIRA not working when a trailing slash is included
+merge_request:
+author:
+type: fixed
diff --git a/config/application.rb b/config/application.rb
index 2067428ff62..751307de975 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -6,6 +6,7 @@ Bundler.require(:default, Rails.env)
module Gitlab
class Application < Rails::Application
+ require_dependency Rails.root.join('lib/gitlab/redis/wrapper')
require_dependency Rails.root.join('lib/gitlab/redis/cache')
require_dependency Rails.root.join('lib/gitlab/redis/queues')
require_dependency Rails.root.join('lib/gitlab/redis/shared_state')
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 76f33b765d3..bbd2d214fe4 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -169,15 +169,11 @@ For Omnibus GitLab packages:
1. [Reconfigure GitLab] for the changes to take effect
-#### Digital Ocean Spaces and other S3-compatible providers
+#### Digital Ocean Spaces
-Not all S3 providers are fully-compatible with the Fog library. For example,
-if you see `411 Length Required` errors after attempting to upload, you may
-need to downgrade the `aws_signature_version` value from the default value to
-2 [due to this issue](https://github.com/fog/fog-aws/issues/428).
+This example can be used for a bucket in Amsterdam (AMS3).
-1. For example, with [Digital Ocean Spaces](https://www.digitalocean.com/products/spaces/),
-this example configuration can be used for a bucket in Amsterdam (AMS3):
+1. Add the following to `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['backup_upload_connection'] = {
@@ -185,7 +181,6 @@ this example configuration can be used for a bucket in Amsterdam (AMS3):
'region' => 'ams3',
'aws_access_key_id' => 'AKIAKIAKI',
'aws_secret_access_key' => 'secret123',
- 'aws_signature_version' => 2,
'endpoint' => 'https://ams3.digitaloceanspaces.com'
}
gitlab_rails['backup_upload_remote_directory'] = 'my.s3.bucket'
@@ -193,6 +188,13 @@ this example configuration can be used for a bucket in Amsterdam (AMS3):
1. [Reconfigure GitLab] for the changes to take effect
+#### Other S3 Providers
+
+Not all S3 providers are fully-compatible with the Fog library. For example,
+if you see `411 Length Required` errors after attempting to upload, you may
+need to downgrade the `aws_signature_version` value from the default value to
+2 [due to this issue](https://github.com/fog/fog-aws/issues/428).
+
---
For installations from source:
@@ -494,7 +496,7 @@ more of the following options:
- `BACKUP=timestamp_of_backup` - Required if more than one backup exists.
Read what the [backup timestamp is about](#backup-timestamp).
-- `force=yes` - Do not ask if the authorized_keys file should get regenerated.
+- `force=yes` - Does not ask if the authorized_keys file should get regenerated and assumes 'yes' for warning that database tables will be removed.
### Restore for installation from source
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 7b9a80a234b..cb222697f32 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -507,7 +507,15 @@ 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|
+ merge_request.check_if_can_be_merged
+ merge_request.merge_status
+ end
expose :diff_head_sha, as: :sha
expose :merge_commit_sha
expose :user_notes_count
diff --git a/lib/gitlab/git/blame.rb b/lib/gitlab/git/blame.rb
index 31effdba292..6d6ed065f79 100644
--- a/lib/gitlab/git/blame.rb
+++ b/lib/gitlab/git/blame.rb
@@ -42,9 +42,7 @@ module Gitlab
end
def load_blame_by_shelling_out
- cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{@repo.path} blame -p #{@sha} -- #{@path})
- # Read in binary mode to ensure ASCII-8BIT
- IO.popen(cmd, 'rb') {|io| io.read }
+ @repo.shell_blame(@sha, @path)
end
def process_raw_blame(output)
diff --git a/lib/gitlab/git/popen.rb b/lib/gitlab/git/popen.rb
index 1ccca13ce2f..e0bd2bbe47b 100644
--- a/lib/gitlab/git/popen.rb
+++ b/lib/gitlab/git/popen.rb
@@ -19,6 +19,8 @@ module Gitlab
cmd_output = ""
cmd_status = 0
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
+ stdout.set_encoding(Encoding::ASCII_8BIT)
+
yield(stdin) if block_given?
stdin.close
diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb
index 638d335b523..3a7930154e5 100644
--- a/lib/gitlab/git/repository.rb
+++ b/lib/gitlab/git/repository.rb
@@ -468,9 +468,13 @@ module Gitlab
}
options = default_options.merge(options)
- options[:limit] ||= 0
options[:offset] ||= 0
+ limit = options[:limit]
+ if limit == 0 || !limit.is_a?(Integer)
+ raise ArgumentError.new("invalid Repository#log limit: #{limit.inspect}")
+ end
+
gitaly_migrate(:find_commits) do |is_enabled|
if is_enabled
gitaly_commit_client.find_commits(options)
@@ -614,11 +618,11 @@ module Gitlab
if is_enabled
gitaly_ref_client.find_ref_name(sha, ref_path)
else
- args = %W(#{Gitlab.config.git.bin_path} for-each-ref --count=1 #{ref_path} --contains #{sha})
+ args = %W(for-each-ref --count=1 #{ref_path} --contains #{sha})
# Not found -> ["", 0]
# Found -> ["b8d95eb4969eefacb0a58f6a28f6803f8070e7b9 commit\trefs/environments/production/77\n", 0]
- popen(args, @path).first.split.last
+ run_git(args).first.split.last
end
end
end
@@ -887,8 +891,7 @@ module Gitlab
"delete #{ref}\x00\x00"
end
- command = %W[#{Gitlab.config.git.bin_path} update-ref --stdin -z]
- message, status = popen(command, path) do |stdin|
+ message, status = run_git(%w[update-ref --stdin -z]) do |stdin|
stdin.write(instructions.join)
end
@@ -1409,6 +1412,11 @@ module Gitlab
end
end
+ def shell_blame(sha, path)
+ output, _status = run_git(%W(blame -p #{sha} -- #{path}))
+ output
+ end
+
private
def shell_write_ref(ref_path, ref, old_ref)
@@ -1433,6 +1441,12 @@ module Gitlab
def run_git(args, chdir: path, env: {}, nice: false, &block)
cmd = [Gitlab.config.git.bin_path, *args]
cmd.unshift("nice") if nice
+
+ object_directories = alternate_object_directories
+ if object_directories.any?
+ env['GIT_ALTERNATE_OBJECT_DIRECTORIES'] = object_directories.join(File::PATH_SEPARATOR)
+ end
+
circuit_breaker.perform do
popen(cmd, chdir, env, &block)
end
@@ -1624,7 +1638,7 @@ module Gitlab
offset_in_ruby = use_follow_flag && options[:offset].present?
limit += offset if offset_in_ruby
- cmd = %W[#{Gitlab.config.git.bin_path} --git-dir=#{path} log]
+ cmd = %w[log]
cmd << "--max-count=#{limit}"
cmd << '--format=%H'
cmd << "--skip=#{offset}" unless offset_in_ruby
@@ -1640,7 +1654,7 @@ module Gitlab
cmd += Array(options[:path])
end
- raw_output = IO.popen(cmd) { |io| io.read }
+ raw_output, _status = run_git(cmd)
lines = offset_in_ruby ? raw_output.lines.drop(offset) : raw_output.lines
lines.map! { |c| Rugged::Commit.new(rugged, c.strip) }
@@ -1678,18 +1692,23 @@ module Gitlab
end
def alternate_object_directories
- relative_paths = Gitlab::Git::Env.all.values_at(*ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES).flatten.compact
+ relative_paths = relative_object_directories
if relative_paths.any?
relative_paths.map { |d| File.join(path, d) }
else
- Gitlab::Git::Env.all.values_at(*ALLOWED_OBJECT_DIRECTORIES_VARIABLES)
- .flatten
- .compact
- .flat_map { |d| d.split(File::PATH_SEPARATOR) }
+ absolute_object_directories.flat_map { |d| d.split(File::PATH_SEPARATOR) }
end
end
+ def relative_object_directories
+ Gitlab::Git::Env.all.values_at(*ALLOWED_OBJECT_RELATIVE_DIRECTORIES_VARIABLES).flatten.compact
+ end
+
+ def absolute_object_directories
+ Gitlab::Git::Env.all.values_at(*ALLOWED_OBJECT_DIRECTORIES_VARIABLES).flatten.compact
+ end
+
# Get the content of a blob for a given commit. If the blob is a commit
# (for submodules) then return the blob's OID.
def blob_content(commit, blob_name)
@@ -1833,13 +1852,13 @@ module Gitlab
def count_commits_by_shelling_out(options)
cmd = count_commits_shelling_command(options)
- raw_output = IO.popen(cmd) { |io| io.read }
+ raw_output, _status = run_git(cmd)
process_count_commits_raw_output(raw_output, options)
end
def count_commits_shelling_command(options)
- cmd = %W[#{Gitlab.config.git.bin_path} --git-dir=#{path} rev-list]
+ cmd = %w[rev-list]
cmd << "--after=#{options[:after].iso8601}" if options[:after]
cmd << "--before=#{options[:before].iso8601}" if options[:before]
cmd << "--max-count=#{options[:max_count]}" if options[:max_count]
@@ -1884,20 +1903,17 @@ module Gitlab
return []
end
- cmd = %W(#{Gitlab.config.git.bin_path} --git-dir=#{path} ls-tree)
- cmd += %w(-r)
- cmd += %w(--full-tree)
- cmd += %w(--full-name)
- cmd += %W(-- #{actual_ref})
+ cmd = %W(ls-tree -r --full-tree --full-name -- #{actual_ref})
+ raw_output, _status = run_git(cmd)
- raw_output = IO.popen(cmd, &:read).split("\n").map do |f|
+ lines = raw_output.split("\n").map do |f|
stuff, path = f.split("\t")
_mode, type, _sha = stuff.split(" ")
path if type == "blob"
# Contain only blob type
end
- raw_output.compact
+ lines.compact
end
# Returns true if the given ref name exists
diff --git a/lib/gitlab/popen.rb b/lib/gitlab/popen.rb
index 4bc5cda8cb5..b9832a724c4 100644
--- a/lib/gitlab/popen.rb
+++ b/lib/gitlab/popen.rb
@@ -5,7 +5,17 @@ module Gitlab
module Popen
extend self
- def popen(cmd, path = nil, vars = {})
+ Result = Struct.new(:cmd, :stdout, :stderr, :status, :duration)
+
+ # Returns [stdout + stderr, status]
+ def popen(cmd, path = nil, vars = {}, &block)
+ result = popen_with_detail(cmd, path, vars, &block)
+
+ [result.stdout << result.stderr, result.status&.exitstatus]
+ end
+
+ # Returns Result
+ def popen_with_detail(cmd, path = nil, vars = {})
unless cmd.is_a?(Array)
raise "System commands must be given as an array of strings"
end
@@ -18,18 +28,21 @@ module Gitlab
FileUtils.mkdir_p(path)
end
- cmd_output = ""
- cmd_status = 0
+ cmd_stdout = ''
+ cmd_stderr = ''
+ cmd_status = nil
+ start = Time.now
+
Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
yield(stdin) if block_given?
stdin.close
- cmd_output << stdout.read
- cmd_output << stderr.read
- cmd_status = wait_thr.value.exitstatus
+ cmd_stdout = stdout.read
+ cmd_stderr = stderr.read
+ cmd_status = wait_thr.value
end
- [cmd_output, cmd_status]
+ Result.new(cmd, cmd_stdout, cmd_stderr, cmd_status, Time.now - start)
end
end
end
diff --git a/lib/gitlab/popen/runner.rb b/lib/gitlab/popen/runner.rb
new file mode 100644
index 00000000000..f44035a48bb
--- /dev/null
+++ b/lib/gitlab/popen/runner.rb
@@ -0,0 +1,46 @@
+module Gitlab
+ module Popen
+ class Runner
+ attr_reader :results
+
+ def initialize
+ @results = []
+ end
+
+ def run(commands, &block)
+ commands.each do |cmd|
+ # yield doesn't support blocks, so we need to use a block variable
+ block.call(cmd) do # rubocop:disable Performance/RedundantBlockCall
+ cmd_result = Gitlab::Popen.popen_with_detail(cmd)
+
+ results << cmd_result
+
+ cmd_result
+ end
+ end
+ end
+
+ def all_success_and_clean?
+ all_success? && all_stderr_empty?
+ end
+
+ def all_success?
+ results.all? { |result| result.status.success? }
+ end
+
+ def all_stderr_empty?
+ results.all? { |result| result.stderr.empty? }
+ end
+
+ def failed_results
+ results.reject { |result| result.status.success? }
+ end
+
+ def warned_results
+ results.select do |result|
+ result.status.success? && !result.stderr.empty?
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/redis/cache.rb b/lib/gitlab/redis/cache.rb
index 9bf019b72e6..a991933e910 100644
--- a/lib/gitlab/redis/cache.rb
+++ b/lib/gitlab/redis/cache.rb
@@ -1,5 +1,5 @@
# please require all dependencies below:
-require_relative 'wrapper' unless defined?(::Gitlab::Redis::Wrapper)
+require_relative 'wrapper' unless defined?(::Rails) && ::Rails.root.present?
module Gitlab
module Redis
diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb
index 94a481a0f2e..98f005cb61b 100644
--- a/lib/gitlab/seeder.rb
+++ b/lib/gitlab/seeder.rb
@@ -5,9 +5,15 @@ module DeliverNever
end
end
+module MuteNotifications
+ def new_note(note)
+ end
+end
+
module Gitlab
class Seeder
def self.quiet
+ mute_notifications
mute_mailer
SeedFu.quiet = true
@@ -18,6 +24,10 @@ module Gitlab
puts "\nOK".color(:green)
end
+ def self.mute_notifications
+ NotificationService.prepend(MuteNotifications)
+ end
+
def self.mute_mailer
ActionMailer::MessageDelivery.prepend(DeliverNever)
end
diff --git a/lib/tasks/gitlab/task_helpers.rb b/lib/gitlab/task_helpers.rb
index c1182af1014..34bee6fecbe 100644
--- a/lib/tasks/gitlab/task_helpers.rb
+++ b/lib/gitlab/task_helpers.rb
@@ -1,6 +1,7 @@
require 'rainbow/ext/string'
require 'gitlab/utils/strong_memoize'
+# rubocop:disable Rails/Output
module Gitlab
TaskFailedError = Class.new(StandardError)
TaskAbortedByUserError = Class.new(StandardError)
@@ -96,11 +97,9 @@ module Gitlab
end
def gid_for(group_name)
- begin
- Etc.getgrnam(group_name).gid
- rescue ArgumentError # no group
- "group #{group_name} doesn't exist"
- end
+ Etc.getgrnam(group_name).gid
+ rescue ArgumentError # no group
+ "group #{group_name} doesn't exist"
end
def gitlab_user
diff --git a/lib/gitlab/upgrader.rb b/lib/gitlab/upgrader.rb
index 3b64cb32afa..d545f2f95f1 100644
--- a/lib/gitlab/upgrader.rb
+++ b/lib/gitlab/upgrader.rb
@@ -1,6 +1,3 @@
-require_relative "popen"
-require_relative "version_info"
-
module Gitlab
class Upgrader
def execute
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index 54f51d9d633..0e27a28ea6e 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -17,6 +17,8 @@
## See installation.md#using-https for additional HTTPS configuration details.
upstream gitlab-workhorse {
+ # Gitlab socket file,
+ # for Omnibus this would be: unix:/var/opt/gitlab/gitlab-workhorse/socket
server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
}
@@ -110,6 +112,8 @@ server {
error_page 502 /502.html;
error_page 503 /503.html;
location ~ ^/(404|422|500|502|503)\.html$ {
+ # Location to the Gitlab's public directory,
+ # for Omnibus this would be: /opt/gitlab/embedded/service/gitlab-rails/public.
root /home/git/gitlab/public;
internal;
}
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index ed8131ef24f..8218d68f9ba 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -21,6 +21,8 @@
## See installation.md#using-https for additional HTTPS configuration details.
upstream gitlab-workhorse {
+ # Gitlab socket file,
+ # for Omnibus this would be: unix:/var/opt/gitlab/gitlab-workhorse/socket
server unix:/home/git/gitlab/tmp/sockets/gitlab-workhorse.socket fail_timeout=0;
}
@@ -160,6 +162,8 @@ server {
error_page 502 /502.html;
error_page 503 /503.html;
location ~ ^/(404|422|500|502|503)\.html$ {
+ # Location to the Gitlab's public directory,
+ # for Omnibus this would be: /opt/gitlab/embedded/service/gitlab-rails/public
root /home/git/gitlab/public;
internal;
}
diff --git a/lib/system_check/helpers.rb b/lib/system_check/helpers.rb
index c42ae4fe4c4..914ed794601 100644
--- a/lib/system_check/helpers.rb
+++ b/lib/system_check/helpers.rb
@@ -1,5 +1,3 @@
-require 'tasks/gitlab/task_helpers'
-
module SystemCheck
module Helpers
include ::Gitlab::TaskHelpers
diff --git a/lib/tasks/flay.rake b/lib/tasks/flay.rake
index 7ad2b2e4d39..b1e012e70c5 100644
--- a/lib/tasks/flay.rake
+++ b/lib/tasks/flay.rake
@@ -1,6 +1,6 @@
desc 'Code duplication analyze via flay'
task :flay do
- output = `bundle exec flay --mass 35 app/ lib/gitlab/`
+ output = `bundle exec flay --mass 35 app/ lib/gitlab/ 2> #{File::NULL}`
if output.include? "Similar code found"
puts output
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index 2383bcf954b..24e37f6c6cc 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -4,7 +4,7 @@ namespace :gitlab do
namespace :backup do
# Create backup of GitLab system
desc "GitLab | Create a backup of the GitLab system"
- task create: :environment do
+ task create: :gitlab_environment do
warn_user_is_not_gitlab
configure_cron_mode
@@ -25,7 +25,7 @@ namespace :gitlab do
# Restore backup of GitLab system
desc 'GitLab | Restore a previously created backup'
- task restore: :environment do
+ task restore: :gitlab_environment do
warn_user_is_not_gitlab
configure_cron_mode
@@ -73,7 +73,7 @@ namespace :gitlab do
end
namespace :repo do
- task create: :environment do
+ task create: :gitlab_environment do
$progress.puts "Dumping repositories ...".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("repositories")
@@ -84,7 +84,7 @@ namespace :gitlab do
end
end
- task restore: :environment do
+ task restore: :gitlab_environment do
$progress.puts "Restoring repositories ...".color(:blue)
Backup::Repository.new.restore
$progress.puts "done".color(:green)
@@ -92,7 +92,7 @@ namespace :gitlab do
end
namespace :db do
- task create: :environment do
+ task create: :gitlab_environment do
$progress.puts "Dumping database ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("db")
@@ -103,7 +103,7 @@ namespace :gitlab do
end
end
- task restore: :environment do
+ task restore: :gitlab_environment do
$progress.puts "Restoring database ... ".color(:blue)
Backup::Database.new.restore
$progress.puts "done".color(:green)
@@ -111,7 +111,7 @@ namespace :gitlab do
end
namespace :builds do
- task create: :environment do
+ task create: :gitlab_environment do
$progress.puts "Dumping builds ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("builds")
@@ -122,7 +122,7 @@ namespace :gitlab do
end
end
- task restore: :environment do
+ task restore: :gitlab_environment do
$progress.puts "Restoring builds ... ".color(:blue)
Backup::Builds.new.restore
$progress.puts "done".color(:green)
@@ -130,7 +130,7 @@ namespace :gitlab do
end
namespace :uploads do
- task create: :environment do
+ task create: :gitlab_environment do
$progress.puts "Dumping uploads ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("uploads")
@@ -141,7 +141,7 @@ namespace :gitlab do
end
end
- task restore: :environment do
+ task restore: :gitlab_environment do
$progress.puts "Restoring uploads ... ".color(:blue)
Backup::Uploads.new.restore
$progress.puts "done".color(:green)
@@ -149,7 +149,7 @@ namespace :gitlab do
end
namespace :artifacts do
- task create: :environment do
+ task create: :gitlab_environment do
$progress.puts "Dumping artifacts ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("artifacts")
@@ -160,7 +160,7 @@ namespace :gitlab do
end
end
- task restore: :environment do
+ task restore: :gitlab_environment do
$progress.puts "Restoring artifacts ... ".color(:blue)
Backup::Artifacts.new.restore
$progress.puts "done".color(:green)
@@ -168,7 +168,7 @@ namespace :gitlab do
end
namespace :pages do
- task create: :environment do
+ task create: :gitlab_environment do
$progress.puts "Dumping pages ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("pages")
@@ -179,7 +179,7 @@ namespace :gitlab do
end
end
- task restore: :environment do
+ task restore: :gitlab_environment do
$progress.puts "Restoring pages ... ".color(:blue)
Backup::Pages.new.restore
$progress.puts "done".color(:green)
@@ -187,7 +187,7 @@ namespace :gitlab do
end
namespace :lfs do
- task create: :environment do
+ task create: :gitlab_environment do
$progress.puts "Dumping lfs objects ... ".color(:blue)
if ENV["SKIP"] && ENV["SKIP"].include?("lfs")
@@ -198,7 +198,7 @@ namespace :gitlab do
end
end
- task restore: :environment do
+ task restore: :gitlab_environment do
$progress.puts "Restoring lfs objects ... ".color(:blue)
Backup::Lfs.new.restore
$progress.puts "done".color(:green)
@@ -206,7 +206,7 @@ namespace :gitlab do
end
namespace :registry do
- task create: :environment do
+ task create: :gitlab_environment do
$progress.puts "Dumping container registry images ... ".color(:blue)
if Gitlab.config.registry.enabled
@@ -221,7 +221,7 @@ namespace :gitlab do
end
end
- task restore: :environment do
+ task restore: :gitlab_environment do
$progress.puts "Restoring container registry images ... ".color(:blue)
if Gitlab.config.registry.enabled
diff --git a/lib/tasks/gitlab/check.rake b/lib/tasks/gitlab/check.rake
index a584eb97cf5..e05a3aad824 100644
--- a/lib/tasks/gitlab/check.rake
+++ b/lib/tasks/gitlab/check.rake
@@ -1,7 +1,3 @@
-# Temporary hack, until we migrate all checks to SystemCheck format
-require 'system_check'
-require 'system_check/helpers'
-
namespace :gitlab do
desc 'GitLab | Check the configuration of GitLab and its environment'
task check: %w{gitlab:gitlab_shell:check
@@ -12,7 +8,7 @@ namespace :gitlab do
namespace :app do
desc 'GitLab | Check the configuration of the GitLab Rails app'
- task check: :environment do
+ task check: :gitlab_environment do
warn_user_is_not_gitlab
checks = [
@@ -43,7 +39,7 @@ namespace :gitlab do
namespace :gitlab_shell do
desc "GitLab | Check the configuration of GitLab Shell"
- task check: :environment do
+ task check: :gitlab_environment do
warn_user_is_not_gitlab
start_checking "GitLab Shell"
@@ -251,7 +247,7 @@ namespace :gitlab do
namespace :sidekiq do
desc "GitLab | Check the configuration of Sidekiq"
- task check: :environment do
+ task check: :gitlab_environment do
warn_user_is_not_gitlab
start_checking "Sidekiq"
@@ -310,7 +306,7 @@ namespace :gitlab do
namespace :incoming_email do
desc "GitLab | Check the configuration of Reply by email"
- task check: :environment do
+ task check: :gitlab_environment do
warn_user_is_not_gitlab
if Gitlab.config.incoming_email.enabled
@@ -333,7 +329,7 @@ namespace :gitlab do
end
namespace :ldap do
- task :check, [:limit] => :environment do |_, args|
+ task :check, [:limit] => :gitlab_environment do |_, args|
# Only show up to 100 results because LDAP directories can be very big.
# This setting only affects the `rake gitlab:check` script.
args.with_defaults(limit: 100)
@@ -389,7 +385,7 @@ namespace :gitlab do
namespace :repo do
desc "GitLab | Check the integrity of the repositories managed by GitLab"
- task check: :environment do
+ task check: :gitlab_environment do
puts "This task is deprecated. Please use gitlab:git:fsck instead".color(:red)
Rake::Task["gitlab:git:fsck"].execute
end
@@ -397,7 +393,7 @@ namespace :gitlab do
namespace :orphans do
desc 'Gitlab | Check for orphaned namespaces and repositories'
- task check: :environment do
+ task check: :gitlab_environment do
warn_user_is_not_gitlab
checks = [
SystemCheck::Orphans::NamespaceCheck,
@@ -408,7 +404,7 @@ namespace :gitlab do
end
desc 'GitLab | Check for orphaned namespaces in the repositories path'
- task check_namespaces: :environment do
+ task check_namespaces: :gitlab_environment do
warn_user_is_not_gitlab
checks = [SystemCheck::Orphans::NamespaceCheck]
@@ -416,7 +412,7 @@ namespace :gitlab do
end
desc 'GitLab | Check for orphaned repositories in the repositories path'
- task check_repositories: :environment do
+ task check_repositories: :gitlab_environment do
warn_user_is_not_gitlab
checks = [SystemCheck::Orphans::RepositoryCheck]
@@ -426,7 +422,7 @@ namespace :gitlab do
namespace :user do
desc "GitLab | Check the integrity of a specific user's repositories"
- task :check_repos, [:username] => :environment do |t, args|
+ task :check_repos, [:username] => :gitlab_environment do |t, args|
username = args[:username] || prompt("Check repository integrity for username? ".color(:blue))
user = User.find_by(username: username)
if user
diff --git a/lib/tasks/gitlab/cleanup.rake b/lib/tasks/gitlab/cleanup.rake
index ab601b0d66b..5a53eac0897 100644
--- a/lib/tasks/gitlab/cleanup.rake
+++ b/lib/tasks/gitlab/cleanup.rake
@@ -5,7 +5,7 @@ namespace :gitlab do
HASHED_REPOSITORY_NAME = '@hashed'.freeze
desc "GitLab | Cleanup | Clean namespaces"
- task dirs: :environment do
+ task dirs: :gitlab_environment do
warn_user_is_not_gitlab
remove_flag = ENV['REMOVE']
@@ -49,7 +49,7 @@ namespace :gitlab do
end
desc "GitLab | Cleanup | Clean repositories"
- task repos: :environment do
+ task repos: :gitlab_environment do
warn_user_is_not_gitlab
move_suffix = "+orphaned+#{Time.now.to_i}"
@@ -78,7 +78,7 @@ namespace :gitlab do
end
desc "GitLab | Cleanup | Block users that have been removed in LDAP"
- task block_removed_ldap_users: :environment do
+ task block_removed_ldap_users: :gitlab_environment do
warn_user_is_not_gitlab
block_flag = ENV['BLOCK']
@@ -109,7 +109,7 @@ namespace :gitlab do
# released. So likely this should only be run once on gitlab.com
# Faulty refs are moved so they are kept around, else some features break.
desc 'GitLab | Cleanup | Remove faulty deployment refs'
- task move_faulty_deployment_refs: :environment do
+ task move_faulty_deployment_refs: :gitlab_environment do
projects = Project.where(id: Deployment.select(:project_id).distinct)
projects.find_each do |project|
diff --git a/lib/tasks/gitlab/git.rake b/lib/tasks/gitlab/git.rake
index 3f5dd2ae3b3..cb4f7e5c8a8 100644
--- a/lib/tasks/gitlab/git.rake
+++ b/lib/tasks/gitlab/git.rake
@@ -1,7 +1,7 @@
namespace :gitlab do
namespace :git do
desc "GitLab | Git | Repack"
- task repack: :environment do
+ task repack: :gitlab_environment do
failures = perform_git_cmd(%W(#{Gitlab.config.git.bin_path} repack -a --quiet), "Repacking repo")
if failures.empty?
puts "Done".color(:green)
@@ -11,7 +11,7 @@ namespace :gitlab do
end
desc "GitLab | Git | Run garbage collection on all repos"
- task gc: :environment do
+ task gc: :gitlab_environment do
failures = perform_git_cmd(%W(#{Gitlab.config.git.bin_path} gc --auto --quiet), "Garbage Collecting")
if failures.empty?
puts "Done".color(:green)
@@ -21,7 +21,7 @@ namespace :gitlab do
end
desc "GitLab | Git | Prune all repos"
- task prune: :environment do
+ task prune: :gitlab_environment do
failures = perform_git_cmd(%W(#{Gitlab.config.git.bin_path} prune), "Git Prune")
if failures.empty?
puts "Done".color(:green)
@@ -31,7 +31,7 @@ namespace :gitlab do
end
desc 'GitLab | Git | Check all repos integrity'
- task fsck: :environment do
+ task fsck: :gitlab_environment do
failures = perform_git_cmd(%W(#{Gitlab.config.git.bin_path} fsck --name-objects --no-progress), "Checking integrity") do |repo|
check_config_lock(repo)
check_ref_locks(repo)
diff --git a/lib/tasks/gitlab/gitaly.rake b/lib/tasks/gitlab/gitaly.rake
index a2e68c0471b..107ff1d8aeb 100644
--- a/lib/tasks/gitlab/gitaly.rake
+++ b/lib/tasks/gitlab/gitaly.rake
@@ -1,7 +1,7 @@
namespace :gitlab do
namespace :gitaly do
desc "GitLab | Install or upgrade gitaly"
- task :install, [:dir, :repo] => :environment do |t, args|
+ task :install, [:dir, :repo] => :gitlab_environment do |t, args|
require 'toml'
warn_user_is_not_gitlab
@@ -21,7 +21,11 @@ namespace :gitlab do
_, status = Gitlab::Popen.popen(%w[which gmake])
command << (status.zero? ? 'gmake' : 'make')
- command << 'BUNDLE_FLAGS=--no-deployment' if Rails.env.test?
+ if Rails.env.test?
+ command.push(
+ 'BUNDLE_FLAGS=--no-deployment',
+ "BUNDLE_PATH=#{Bundler.bundle_path}")
+ end
Gitlab::SetupHelper.create_gitaly_configuration(args.dir)
Dir.chdir(args.dir) do
diff --git a/lib/tasks/gitlab/helpers.rake b/lib/tasks/gitlab/helpers.rake
index b0a24790c4a..14d1125a03d 100644
--- a/lib/tasks/gitlab/helpers.rake
+++ b/lib/tasks/gitlab/helpers.rake
@@ -1,8 +1,6 @@
-require 'tasks/gitlab/task_helpers'
-
# Prevent StateMachine warnings from outputting during a cron task
StateMachines::Machine.ignore_method_conflicts = true if ENV['CRON']
-namespace :gitlab do
+task gitlab_environment: :environment do
extend SystemCheck::Helpers
end
diff --git a/lib/tasks/gitlab/info.rake b/lib/tasks/gitlab/info.rake
index e9fb6a008b0..45e9a1a1c72 100644
--- a/lib/tasks/gitlab/info.rake
+++ b/lib/tasks/gitlab/info.rake
@@ -1,7 +1,7 @@
namespace :gitlab do
namespace :env do
desc "GitLab | Show information about GitLab and its environment"
- task info: :environment do
+ task info: :gitlab_environment do
# check if there is an RVM environment
rvm_version = run_and_match(%w(rvm --version), /[\d\.]+/).try(:to_s)
# check Ruby version
diff --git a/lib/tasks/gitlab/setup.rake b/lib/tasks/gitlab/setup.rake
index 05fcb8e3da5..1d903c81358 100644
--- a/lib/tasks/gitlab/setup.rake
+++ b/lib/tasks/gitlab/setup.rake
@@ -1,6 +1,6 @@
namespace :gitlab do
desc "GitLab | Setup production application"
- task setup: :environment do
+ task setup: :gitlab_environment do
setup_db
end
diff --git a/lib/tasks/gitlab/shell.rake b/lib/tasks/gitlab/shell.rake
index 12ae4199b69..844664b12d4 100644
--- a/lib/tasks/gitlab/shell.rake
+++ b/lib/tasks/gitlab/shell.rake
@@ -1,7 +1,7 @@
namespace :gitlab do
namespace :shell do
desc "GitLab | Install or upgrade gitlab-shell"
- task :install, [:repo] => :environment do |t, args|
+ task :install, [:repo] => :gitlab_environment do |t, args|
warn_user_is_not_gitlab
default_version = Gitlab::Shell.version_required
@@ -58,12 +58,12 @@ namespace :gitlab do
end
desc "GitLab | Setup gitlab-shell"
- task setup: :environment do
+ task setup: :gitlab_environment do
setup
end
desc "GitLab | Build missing projects"
- task build_missing_projects: :environment do
+ task build_missing_projects: :gitlab_environment do
Project.find_each(batch_size: 1000) do |project|
path_to_repo = project.repository.path_to_repo
if File.exist?(path_to_repo)
@@ -80,7 +80,7 @@ namespace :gitlab do
end
desc 'Create or repair repository hooks symlink'
- task create_hooks: :environment do
+ task create_hooks: :gitlab_environment do
warn_user_is_not_gitlab
puts 'Creating/Repairing hooks symlinks for all repositories'
diff --git a/lib/tasks/gitlab/workhorse.rake b/lib/tasks/gitlab/workhorse.rake
index 308ffb0e284..b917a293095 100644
--- a/lib/tasks/gitlab/workhorse.rake
+++ b/lib/tasks/gitlab/workhorse.rake
@@ -1,7 +1,7 @@
namespace :gitlab do
namespace :workhorse do
desc "GitLab | Install or upgrade gitlab-workhorse"
- task :install, [:dir, :repo] => :environment do |t, args|
+ task :install, [:dir, :repo] => :gitlab_environment do |t, args|
warn_user_is_not_gitlab
unless args.dir.present?
diff --git a/lib/tasks/haml-lint.rake b/lib/tasks/haml-lint.rake
index ad2d034b0b4..5c0cc4990fc 100644
--- a/lib/tasks/haml-lint.rake
+++ b/lib/tasks/haml-lint.rake
@@ -2,5 +2,14 @@ unless Rails.env.production?
require 'haml_lint/rake_task'
require 'haml_lint/inline_javascript'
+ # Workaround for warnings from parser/current
+ # TODO: Remove this after we update parser gem
+ task :haml_lint do
+ require 'parser'
+ def Parser.warn(*args)
+ puts(*args) # static-analysis ignores stdout if status is 0
+ end
+ end
+
HamlLint::RakeTask.new
end
diff --git a/lib/tasks/migrate/setup_postgresql.rake b/lib/tasks/migrate/setup_postgresql.rake
index c996537cfbe..31cbd651edb 100644
--- a/lib/tasks/migrate/setup_postgresql.rake
+++ b/lib/tasks/migrate/setup_postgresql.rake
@@ -1,16 +1,14 @@
-require Rails.root.join('lib/gitlab/database')
-require Rails.root.join('lib/gitlab/database/migration_helpers')
-require Rails.root.join('db/migrate/20151007120511_namespaces_projects_path_lower_indexes')
-require Rails.root.join('db/migrate/20151008110232_add_users_lower_username_email_indexes')
-require Rails.root.join('db/migrate/20161212142807_add_lower_path_index_to_routes')
-require Rails.root.join('db/migrate/20170317203554_index_routes_path_for_like')
-require Rails.root.join('db/migrate/20170724214302_add_lower_path_index_to_redirect_routes')
-require Rails.root.join('db/migrate/20170503185032_index_redirect_routes_path_for_like')
-require Rails.root.join('db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb')
-require Rails.root.join('db/migrate/20180113220114_rework_redirect_routes_indexes.rb')
-
desc 'GitLab | Sets up PostgreSQL'
task setup_postgresql: :environment do
+ require Rails.root.join('db/migrate/20151007120511_namespaces_projects_path_lower_indexes')
+ require Rails.root.join('db/migrate/20151008110232_add_users_lower_username_email_indexes')
+ require Rails.root.join('db/migrate/20161212142807_add_lower_path_index_to_routes')
+ require Rails.root.join('db/migrate/20170317203554_index_routes_path_for_like')
+ require Rails.root.join('db/migrate/20170724214302_add_lower_path_index_to_redirect_routes')
+ require Rails.root.join('db/migrate/20170503185032_index_redirect_routes_path_for_like')
+ require Rails.root.join('db/migrate/20171220191323_add_index_on_namespaces_lower_name.rb')
+ require Rails.root.join('db/migrate/20180113220114_rework_redirect_routes_indexes.rb')
+
NamespacesProjectsPathLowerIndexes.new.up
AddUsersLowerUsernameEmailIndexes.new.up
AddLowerPathIndexToRoutes.new.up
diff --git a/qa/qa.rb b/qa/qa.rb
index e4e32de115f..180ee778fd4 100644
--- a/qa/qa.rb
+++ b/qa/qa.rb
@@ -28,6 +28,7 @@ module QA
autoload :Sandbox, 'qa/factory/resource/sandbox'
autoload :Group, 'qa/factory/resource/group'
autoload :Project, 'qa/factory/resource/project'
+ autoload :MergeRequest, 'qa/factory/resource/merge_request'
autoload :DeployKey, 'qa/factory/resource/deploy_key'
autoload :SecretVariable, 'qa/factory/resource/secret_variable'
autoload :Runner, 'qa/factory/resource/runner'
@@ -130,6 +131,10 @@ module QA
autoload :PersonalAccessTokens, 'qa/page/profile/personal_access_tokens'
end
+ module MergeRequest
+ autoload :New, 'qa/page/merge_request/new'
+ end
+
module Admin
autoload :Settings, 'qa/page/admin/settings'
end
diff --git a/qa/qa/factory/dependency.rb b/qa/qa/factory/dependency.rb
index d0e85a68237..fc5dc82ce29 100644
--- a/qa/qa/factory/dependency.rb
+++ b/qa/qa/factory/dependency.rb
@@ -16,20 +16,21 @@ module QA
def build!
return if overridden?
- Builder.new(@signature).fabricate!.tap do |product|
+ Builder.new(@signature, @factory).fabricate!.tap do |product|
@factory.public_send("#{@name}=", product)
end
end
class Builder
- def initialize(signature)
+ def initialize(signature, caller_factory)
@factory = signature.factory
@block = signature.block
+ @caller_factory = caller_factory
end
def fabricate!
@factory.fabricate! do |factory|
- @block&.call(factory)
+ @block&.call(factory, @caller_factory)
end
end
end
diff --git a/qa/qa/factory/resource/merge_request.rb b/qa/qa/factory/resource/merge_request.rb
new file mode 100644
index 00000000000..ce04e904aaf
--- /dev/null
+++ b/qa/qa/factory/resource/merge_request.rb
@@ -0,0 +1,49 @@
+require 'securerandom'
+
+module QA
+ module Factory
+ module Resource
+ class MergeRequest < Factory::Base
+ attr_accessor :title,
+ :description,
+ :source_branch,
+ :target_branch
+
+ dependency Factory::Resource::Project, as: :project do |project|
+ project.name = 'project-with-merge-request'
+ end
+
+ dependency Factory::Repository::Push, as: :target do |push, factory|
+ push.project = factory.project
+ push.branch_name = "master:#{factory.target_branch}"
+ end
+
+ dependency Factory::Repository::Push, as: :source do |push, factory|
+ push.project = factory.project
+ push.branch_name = "#{factory.target_branch}:#{factory.source_branch}"
+ push.file_name = "added_file.txt"
+ push.file_content = "File Added"
+ end
+
+ def initialize
+ @title = 'QA test - merge request'
+ @description = 'This is a test merge request'
+ @source_branch = "qa-test-feature-#{SecureRandom.hex(8)}"
+ @target_branch = "master"
+ end
+
+ def fabricate!
+ project.visit!
+
+ Page::Project::Show.act { new_merge_request }
+
+ Page::MergeRequest::New.perform do |page|
+ page.fill_title(@title)
+ page.fill_description(@description)
+ page.create_merge_request
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/base.rb b/qa/qa/page/base.rb
index 81ba80cdbaf..f472e8ccc7e 100644
--- a/qa/qa/page/base.rb
+++ b/qa/qa/page/base.rb
@@ -13,16 +13,18 @@ module QA
visit current_url
end
- def wait(css = '.application', time: 60)
- Time.now.tap do |start|
- while Time.now - start < time
- break if page.has_css?(css, wait: 5)
+ def wait(max: 60, time: 1, reload: true)
+ start = Time.now
- refresh
- end
+ while Time.now - start < max
+ return true if yield
+
+ sleep(time)
+
+ refresh if reload
end
- yield if block_given?
+ false
end
def scroll_to(selector, text: nil)
@@ -40,12 +42,16 @@ module QA
page.within(selector) { yield } if block_given?
end
+ def find_element(name)
+ find(element_selector_css(name))
+ end
+
def click_element(name)
find_element(name).click
end
- def find_element(name)
- find(element_selector_css(name))
+ def fill_element(name, content)
+ find_element(name).set(content)
end
def within_element(name)
diff --git a/qa/qa/page/group/show.rb b/qa/qa/page/group/show.rb
index 37ed3b35bce..f23294145dd 100644
--- a/qa/qa/page/group/show.rb
+++ b/qa/qa/page/group/show.rb
@@ -25,7 +25,12 @@ module QA
def go_to_new_subgroup
within '.new-project-subgroup' do
- find('.dropdown-toggle').click
+ # May need to click again because it is possible to click the button quicker than the JS is bound
+ wait(reload: false) do
+ find('.dropdown-toggle').click
+
+ page.has_css?("li[data-value='new-subgroup']")
+ end
find("li[data-value='new-subgroup']").click
end
@@ -34,7 +39,12 @@ module QA
def go_to_new_project
within '.new-project-subgroup' do
- find('.dropdown-toggle').click
+ # May need to click again because it is possible to click the button quicker than the JS is bound
+ wait(reload: false) do
+ find('.dropdown-toggle').click
+
+ page.has_css?("li[data-value='new-project']")
+ end
find("li[data-value='new-project']").click
end
diff --git a/qa/qa/page/main/login.rb b/qa/qa/page/main/login.rb
index 9cff2c5c317..95880475ffa 100644
--- a/qa/qa/page/main/login.rb
+++ b/qa/qa/page/main/login.rb
@@ -10,12 +10,14 @@ module QA
view 'app/views/devise/sessions/_new_base.html.haml' do
element :login_field, 'text_field :login'
- element :passowrd_field, 'password_field :password'
+ element :password_field, 'password_field :password'
element :sign_in_button, 'submit "Sign in"'
end
def initialize
- wait('.application', time: 500)
+ wait(max: 500) do
+ page.has_css?('.application')
+ end
end
def sign_in_using_credentials
diff --git a/qa/qa/page/merge_request/new.rb b/qa/qa/page/merge_request/new.rb
new file mode 100644
index 00000000000..ec94ff4ac98
--- /dev/null
+++ b/qa/qa/page/merge_request/new.rb
@@ -0,0 +1,31 @@
+module QA
+ module Page
+ module MergeRequest
+ class New < Page::Base
+ view 'app/views/shared/issuable/_form.html.haml' do
+ element :issuable_create_button
+ end
+
+ view 'app/views/shared/issuable/form/_title.html.haml' do
+ element :issuable_form_title
+ end
+
+ view 'app/views/shared/form_elements/_description.html.haml' do
+ element :issuable_form_description
+ end
+
+ def create_merge_request
+ click_element :issuable_create_button
+ end
+
+ def fill_title(title)
+ fill_element :issuable_form_title, title
+ end
+
+ def fill_description(description)
+ fill_element :issuable_form_description, description
+ end
+ end
+ end
+ end
+end
diff --git a/qa/qa/page/project/new.rb b/qa/qa/page/project/new.rb
index 9b1438f76d5..186a4724326 100644
--- a/qa/qa/page/project/new.rb
+++ b/qa/qa/page/project/new.rb
@@ -4,7 +4,7 @@ module QA
class New < Page::Base
view 'app/views/projects/_new_project_fields.html.haml' do
element :project_namespace_select
- element :project_namespace_field, 'select :namespace_id'
+ element :project_namespace_field, /select :namespace_id.*class: 'select2/
element :project_path, 'text_field :path'
element :project_description, 'text_area :description'
element :project_create_button, "submit 'Create project'"
@@ -13,7 +13,7 @@ module QA
def choose_test_namespace
click_element :project_namespace_select
- first('li', text: Runtime::Namespace.path).click
+ find('ul.select2-result-sub > li', text: Runtime::Namespace.path).click
end
def choose_name(name)
diff --git a/qa/qa/page/project/settings/common.rb b/qa/qa/page/project/settings/common.rb
index c7955124ef3..319cb1045b6 100644
--- a/qa/qa/page/project/settings/common.rb
+++ b/qa/qa/page/project/settings/common.rb
@@ -17,7 +17,12 @@ module QA
def expand_section(name)
page.within('#content-body') do
page.within('section', text: name) do
- click_button 'Expand' unless first('button', text: 'Collapse')
+ # Because it is possible to click the button before the JS toggle code is bound
+ wait(reload: false) do
+ click_button 'Expand' unless first('button', text: 'Collapse')
+
+ page.has_content?('Collapse')
+ end
yield if block_given?
end
diff --git a/qa/qa/page/project/show.rb b/qa/qa/page/project/show.rb
index 5e66e40a0b5..75308ae8a3c 100644
--- a/qa/qa/page/project/show.rb
+++ b/qa/qa/page/project/show.rb
@@ -3,12 +3,14 @@ module QA
module Project
class Show < Page::Base
view 'app/views/shared/_clone_panel.html.haml' do
+ element :clone_holder, '.git-clone-holder'
element :clone_dropdown
element :clone_options_dropdown, '.clone-options-dropdown'
+ element :project_repository_location, 'text_field_tag :project_clone'
end
- view 'app/views/shared/_clone_panel.html.haml' do
- element :project_repository_location, 'text_field_tag :project_clone'
+ view 'app/views/projects/_last_push.html.haml' do
+ element :create_merge_request
end
view 'app/views/projects/_home_panel.html.haml' do
@@ -16,10 +18,15 @@ module QA
end
def choose_repository_clone_http
- click_element :clone_dropdown
+ wait(reload: false) do
+ click_element :clone_dropdown
- page.within('.clone-options-dropdown') do
- click_link('HTTP')
+ page.within('.clone-options-dropdown') do
+ click_link('HTTP')
+ end
+
+ # Ensure git clone textbox was updated to http URI
+ page.has_css?('.git-clone-holder input#project_clone[value*="http"]')
end
end
@@ -31,6 +38,10 @@ module QA
find('.qa-project-name').text
end
+ def new_merge_request
+ click_element :create_merge_request
+ end
+
def wait_for_push
sleep 5
refresh
diff --git a/qa/qa/specs/features/merge_request/create_spec.rb b/qa/qa/specs/features/merge_request/create_spec.rb
new file mode 100644
index 00000000000..fbf9a4d17e5
--- /dev/null
+++ b/qa/qa/specs/features/merge_request/create_spec.rb
@@ -0,0 +1,17 @@
+module QA
+ feature 'creates a merge request', :core do
+ scenario 'user creates a new merge request' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.act { sign_in_using_credentials }
+
+ Factory::Resource::MergeRequest.fabricate! do |merge_request|
+ merge_request.title = 'This is a merge request'
+ merge_request.description = 'Great feature'
+ end
+
+ expect(page).to have_content('This is a merge request')
+ expect(page).to have_content('Great feature')
+ expect(page).to have_content('Opened less than a minute ago')
+ end
+ end
+end
diff --git a/qa/spec/factory/dependency_spec.rb b/qa/spec/factory/dependency_spec.rb
index 32405415126..8aaa6665a18 100644
--- a/qa/spec/factory/dependency_spec.rb
+++ b/qa/spec/factory/dependency_spec.rb
@@ -54,6 +54,19 @@ describe QA::Factory::Dependency do
expect(factory).to have_received(:mydep=).with(dependency)
end
+
+ context 'when receives a caller factory as block argument' do
+ let(:dependency) { QA::Factory::Base }
+
+ it 'calls given block with dependency factory and caller factory' do
+ allow_any_instance_of(QA::Factory::Base).to receive(:fabricate!).and_return(factory)
+ allow(QA::Factory::Product).to receive(:populate!).and_return(spy('any'))
+
+ subject.build!
+
+ expect(block).to have_received(:call).with(an_instance_of(QA::Factory::Base), factory)
+ end
+ end
end
end
end
diff --git a/scripts/static-analysis b/scripts/static-analysis
index 96d08287ded..b3895292ab4 100755
--- a/scripts/static-analysis
+++ b/scripts/static-analysis
@@ -1,6 +1,29 @@
#!/usr/bin/env ruby
-require ::File.expand_path('../lib/gitlab/popen', __dir__)
+# We don't have auto-loading here
+require_relative '../lib/gitlab/popen'
+require_relative '../lib/gitlab/popen/runner'
+
+def emit_warnings(static_analysis)
+ static_analysis.warned_results.each do |result|
+ puts
+ puts "**** #{result.cmd.join(' ')} had the following warnings:"
+ puts
+ puts result.stderr
+ puts
+ end
+end
+
+def emit_errors(static_analysis)
+ static_analysis.failed_results.each do |result|
+ puts
+ puts "**** #{result.cmd.join(' ')} failed with the following error:"
+ puts
+ puts result.stdout
+ puts result.stderr
+ puts
+ end
+end
tasks = [
%w[bundle exec rake config_lint],
@@ -17,18 +40,16 @@ tasks = [
%w[scripts/lint-rugged]
]
-failed_tasks = tasks.reduce({}) do |failures, task|
- start = Time.now
- puts
- puts "$ #{task.join(' ')}"
+static_analysis = Gitlab::Popen::Runner.new
- output, status = Gitlab::Popen.popen(task)
- puts "==> Finished in #{Time.now - start} seconds"
+static_analysis.run(tasks) do |cmd, &run|
puts
+ puts "$ #{cmd.join(' ')}"
- failures[task.join(' ')] = output unless status.zero?
+ result = run.call
- failures
+ puts "==> Finished in #{result.duration} seconds"
+ puts
end
puts
@@ -36,17 +57,20 @@ puts '==================================================='
puts
puts
-if failed_tasks.empty?
+if static_analysis.all_success_and_clean?
puts 'All static analyses passed successfully.'
+elsif static_analysis.all_success?
+ puts 'All static analyses passed successfully, but we have warnings:'
+ puts
+
+ emit_warnings(static_analysis)
+
+ exit 2
else
puts 'Some static analyses failed:'
- failed_tasks.each do |failed_task, output|
- puts
- puts "**** #{failed_task} failed with the following error:"
- puts
- puts output
- end
+ emit_warnings(static_analysis)
+ emit_errors(static_analysis)
exit 1
end
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index a28b8905b65..62a2ec55b00 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -194,7 +194,7 @@ describe 'Commits' do
end
it 'includes the committed_date for each commit' do
- commits = project.repository.commits(branch_name)
+ commits = project.repository.commits(branch_name, limit: 40)
commits.each do |commit|
expect(page).to have_content("authored #{commit.authored_date.strftime("%b %d, %Y")}")
diff --git a/spec/features/projects/blobs/edit_spec.rb b/spec/features/projects/blobs/edit_spec.rb
index 69e4c9f04a1..89d3bd24b89 100644
--- a/spec/features/projects/blobs/edit_spec.rb
+++ b/spec/features/projects/blobs/edit_spec.rb
@@ -17,12 +17,15 @@ feature 'Editing file blob', :js do
sign_in(user)
end
- def edit_and_commit
+ def edit_and_commit(commit_changes: true)
wait_for_requests
find('.js-edit-blob').click
find('#editor')
execute_script('ace.edit("editor").setValue("class NextFeature\nend\n")')
- click_button 'Commit changes'
+
+ if commit_changes
+ click_button 'Commit changes'
+ end
end
context 'from MR diff' do
@@ -39,13 +42,26 @@ feature 'Editing file blob', :js do
context 'from blob file path' do
before do
visit project_blob_path(project, tree_join(branch, file_path))
- edit_and_commit
end
it 'updates content' do
+ edit_and_commit
+
expect(page).to have_content 'successfully committed'
expect(page).to have_content 'NextFeature'
end
+
+ it 'previews content' do
+ edit_and_commit(commit_changes: false)
+ click_link 'Preview changes'
+ wait_for_requests
+
+ old_line_count = page.all('.line_holder.old').size
+ new_line_count = page.all('.line_holder.new').size
+
+ expect(old_line_count).to be > 0
+ expect(new_line_count).to be > 0
+ end
end
end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index da0343588ef..f7a4a7afced 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -100,7 +100,7 @@ describe ApplicationHelper do
end
it 'returns a generic avatar' do
- expect(helper.gravatar_icon(user_email)).to match('no_avatar.png')
+ expect(helper.gravatar_icon(user_email)).to match_asset_path('no_avatar.png')
end
end
@@ -110,7 +110,7 @@ describe ApplicationHelper do
end
it 'returns a generic avatar when email is blank' do
- expect(helper.gravatar_icon('')).to match('no_avatar.png')
+ expect(helper.gravatar_icon('')).to match_asset_path('no_avatar.png')
end
it 'returns a valid Gravatar URL' do
diff --git a/spec/javascripts/pipelines/pipelines_table_row_spec.js b/spec/javascripts/pipelines/pipelines_table_row_spec.js
index b3cbf9aba48..de744739e42 100644
--- a/spec/javascripts/pipelines/pipelines_table_row_spec.js
+++ b/spec/javascripts/pipelines/pipelines_table_row_spec.js
@@ -26,8 +26,8 @@ describe('Pipelines Table Row', () => {
const pipelines = getJSONFixture(jsonFixtureName).pipelines;
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);
+ 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/vue_mr_widget/components/mr_widget_header_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
index 06f89fabf42..93bb83ca8bd 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_header_spec.js
@@ -48,20 +48,23 @@ describe('MRWidgetHeader', () => {
describe('template', () => {
let vm;
let el;
+ let mr;
const sourceBranchPath = '/foo/bar/mr-widget-refactor';
- const mr = {
- divergedCommitsCount: 12,
- sourceBranch: 'mr-widget-refactor',
- sourceBranchLink: `<a href="${sourceBranchPath}">mr-widget-refactor</a>`,
- targetBranchPath: 'foo/bar/commits-path',
- targetBranchTreePath: 'foo/bar/tree/path',
- targetBranch: 'master',
- isOpen: true,
- emailPatchesPath: '/mr/email-patches',
- plainDiffPath: '/mr/plainDiffPath',
- };
beforeEach(() => {
+ mr = {
+ divergedCommitsCount: 12,
+ sourceBranch: 'mr-widget-refactor',
+ sourceBranchLink: `<a href="${sourceBranchPath}">mr-widget-refactor</a>`,
+ sourceBranchRemoved: false,
+ targetBranchPath: 'foo/bar/commits-path',
+ targetBranchTreePath: 'foo/bar/tree/path',
+ targetBranch: 'master',
+ isOpen: true,
+ emailPatchesPath: '/mr/email-patches',
+ plainDiffPath: '/mr/plainDiffPath',
+ };
+
vm = createComponent(mr);
el = vm.$el;
});
@@ -82,6 +85,8 @@ describe('MRWidgetHeader', () => {
expect(el.textContent).toContain('Check out branch');
expect(el.querySelectorAll('.dropdown li a')[0].getAttribute('href')).toEqual(mr.emailPatchesPath);
expect(el.querySelectorAll('.dropdown li a')[1].getAttribute('href')).toEqual(mr.plainDiffPath);
+
+ expect(el.querySelector('a[href="#modal_merge_info"]').getAttribute('disabled')).toBeNull();
});
it('should not have right action links if the MR state is not open', (done) => {
@@ -101,5 +106,16 @@ describe('MRWidgetHeader', () => {
done();
});
});
+
+ it('should disable check out branch button if source branch has been removed', (done) => {
+ vm.mr.sourceBranchRemoved = true;
+
+ Vue.nextTick()
+ .then(() => {
+ expect(el.querySelector('a[href="#modal_merge_info"]').getAttribute('disabled')).toBe('disabled');
+ done();
+ })
+ .catch(done.fail);
+ });
});
});
diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
index 2dc3b72ea40..43a989393ba 100644
--- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_merged_spec.js
@@ -1,108 +1,99 @@
import Vue from 'vue';
-import mergedComponent from '~/vue_merge_request_widget/components/states/mr_widget_merged';
+import mergedComponent from '~/vue_merge_request_widget/components/states/mr_widget_merged.vue';
import eventHub from '~/vue_merge_request_widget/event_hub';
-
-const targetBranch = 'foo';
-
-const createComponent = () => {
- const Component = Vue.extend(mergedComponent);
- const mr = {
- isRemovingSourceBranch: false,
- cherryPickInForkPath: false,
- canCherryPickInCurrentMR: true,
- revertInForkPath: false,
- canRevertInCurrentMR: true,
- canRemoveSourceBranch: true,
- sourceBranchRemoved: true,
- metrics: {
- mergedBy: {},
- mergedAt: 'mergedUpdatedAt',
- readableMergedAt: '',
- closedBy: {},
- closedAt: 'mergedUpdatedAt',
- readableClosedAt: '',
- },
- updatedAt: 'mrUpdatedAt',
- targetBranch,
- };
-
- const service = {
- removeSourceBranch() {},
- };
-
- return new Component({
- el: document.createElement('div'),
- propsData: { mr, service },
- });
-};
+import mountComponent from '../../../helpers/vue_mount_component_helper';
describe('MRWidgetMerged', () => {
- describe('props', () => {
- it('should have props', () => {
- const { mr, service } = mergedComponent.props;
-
- expect(mr.type instanceof Object).toBeTruthy();
- expect(mr.required).toBeTruthy();
-
- expect(service.type instanceof Object).toBeTruthy();
- expect(service.required).toBeTruthy();
- });
- });
-
- describe('components', () => {
- it('should have components added', () => {
- expect(mergedComponent.components['mr-widget-author-and-time']).toBeDefined();
- });
+ let vm;
+ const targetBranch = 'foo';
+
+ beforeEach(() => {
+ const Component = Vue.extend(mergedComponent);
+ const mr = {
+ isRemovingSourceBranch: false,
+ cherryPickInForkPath: false,
+ canCherryPickInCurrentMR: true,
+ revertInForkPath: false,
+ canRevertInCurrentMR: true,
+ canRemoveSourceBranch: true,
+ sourceBranchRemoved: true,
+ metrics: {
+ mergedBy: {
+ name: 'Administrator',
+ username: 'root',
+ webUrl: 'http://localhost:3000/root',
+ avatarUrl: 'http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon',
+ },
+ mergedAt: 'Jan 24, 2018 1:02pm GMT+0000',
+ readableMergedAt: '',
+ closedBy: {},
+ closedAt: 'Jan 24, 2018 1:02pm GMT+0000',
+ readableClosedAt: '',
+ },
+ updatedAt: 'mergedUpdatedAt',
+ targetBranch,
+ };
+
+ const service = {
+ removeSourceBranch() {},
+ };
+
+ spyOn(eventHub, '$emit');
+
+ vm = mountComponent(Component, { mr, service });
});
- describe('data', () => {
- it('should have default data', () => {
- const data = mergedComponent.data();
-
- expect(data.isMakingRequest).toBeFalsy();
- });
+ afterEach(() => {
+ vm.$destroy();
});
describe('computed', () => {
describe('shouldShowRemoveSourceBranch', () => {
- it('should correct value when fields changed', () => {
- const vm = createComponent();
+ it('returns true when sourceBranchRemoved is false', () => {
vm.mr.sourceBranchRemoved = false;
- expect(vm.shouldShowRemoveSourceBranch).toBeTruthy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(true);
+ });
+ it('returns false wehn sourceBranchRemoved is true', () => {
vm.mr.sourceBranchRemoved = true;
- expect(vm.shouldShowRemoveSourceBranch).toBeFalsy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
+ });
+ it('returns false when canRemoveSourceBranch is false', () => {
vm.mr.sourceBranchRemoved = false;
vm.mr.canRemoveSourceBranch = false;
- expect(vm.shouldShowRemoveSourceBranch).toBeFalsy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
+ });
+ it('returns false when is making request', () => {
vm.mr.canRemoveSourceBranch = true;
vm.isMakingRequest = true;
- expect(vm.shouldShowRemoveSourceBranch).toBeFalsy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
+ });
+ it('returns true when all are true', () => {
vm.mr.isRemovingSourceBranch = true;
vm.mr.canRemoveSourceBranch = true;
vm.isMakingRequest = true;
- expect(vm.shouldShowRemoveSourceBranch).toBeFalsy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
});
});
+
describe('shouldShowSourceBranchRemoving', () => {
it('should correct value when fields changed', () => {
- const vm = createComponent();
vm.mr.sourceBranchRemoved = false;
- expect(vm.shouldShowSourceBranchRemoving).toBeFalsy();
+ expect(vm.shouldShowSourceBranchRemoving).toEqual(false);
vm.mr.sourceBranchRemoved = true;
- expect(vm.shouldShowRemoveSourceBranch).toBeFalsy();
+ expect(vm.shouldShowRemoveSourceBranch).toEqual(false);
vm.mr.sourceBranchRemoved = false;
vm.isMakingRequest = true;
- expect(vm.shouldShowSourceBranchRemoving).toBeTruthy();
+ expect(vm.shouldShowSourceBranchRemoving).toEqual(true);
vm.isMakingRequest = false;
vm.mr.isRemovingSourceBranch = true;
- expect(vm.shouldShowSourceBranchRemoving).toBeTruthy();
+ expect(vm.shouldShowSourceBranchRemoving).toEqual(true);
});
});
});
@@ -110,8 +101,6 @@ describe('MRWidgetMerged', () => {
describe('methods', () => {
describe('removeSourceBranch', () => {
it('should set flag and call service then request main component to update the widget', (done) => {
- const vm = createComponent();
- spyOn(eventHub, '$emit');
spyOn(vm.service, 'removeSourceBranch').and.returnValue(new Promise((resolve) => {
resolve({
data: {
@@ -123,7 +112,7 @@ describe('MRWidgetMerged', () => {
vm.removeSourceBranch();
setTimeout(() => {
const args = eventHub.$emit.calls.argsFor(0);
- expect(vm.isMakingRequest).toBeTruthy();
+ expect(vm.isMakingRequest).toEqual(true);
expect(args[0]).toEqual('MRWidgetUpdateRequested');
expect(args[1]).not.toThrow();
done();
@@ -132,53 +121,50 @@ describe('MRWidgetMerged', () => {
});
});
- describe('template', () => {
- let vm;
- let el;
+ it('has merged by information', () => {
+ expect(vm.$el.textContent).toContain('Merged by');
+ expect(vm.$el.textContent).toContain('Administrator');
+ });
- beforeEach(() => {
- vm = createComponent();
- el = vm.$el;
- });
+ it('renders branch information', () => {
+ expect(vm.$el.textContent).toContain('The changes were merged into');
+ expect(vm.$el.textContent).toContain(targetBranch);
+ });
- it('should have correct elements', () => {
- expect(el.classList.contains('mr-widget-body')).toBeTruthy();
- expect(el.querySelector('.js-mr-widget-author')).toBeDefined();
- expect(el.innerText).toContain('The changes were merged into');
- expect(el.innerText).toContain(targetBranch);
- expect(el.innerText).toContain('The source branch has been removed');
- expect(el.innerText).toContain('Revert');
- expect(el.innerText).toContain('Cherry-pick');
- expect(el.innerText).not.toContain('You can remove source branch now');
- expect(el.innerText).not.toContain('The source branch is being removed');
- });
+ it('renders information about branch being removed', () => {
+ expect(vm.$el.textContent).toContain('The source branch has been removed');
+ });
- it('should not show source branch removed text', (done) => {
- vm.mr.sourceBranchRemoved = false;
+ it('shows revert and cherry-pick buttons', () => {
+ expect(vm.$el.textContent).toContain('Revert');
+ expect(vm.$el.textContent).toContain('Cherry-pick');
+ });
- Vue.nextTick(() => {
- expect(el.innerText).toContain('You can remove source branch now');
- expect(el.innerText).not.toContain('The source branch has been removed');
- done();
- });
+ it('should not show source branch removed text', (done) => {
+ vm.mr.sourceBranchRemoved = false;
+
+ Vue.nextTick(() => {
+ expect(vm.$el.innerText).toContain('You can remove source branch now');
+ expect(vm.$el.innerText).not.toContain('The source branch has been removed');
+ done();
});
+ });
- it('should show source branch removing text', (done) => {
- vm.mr.isRemovingSourceBranch = true;
- vm.mr.sourceBranchRemoved = false;
+ it('should show source branch removing text', (done) => {
+ vm.mr.isRemovingSourceBranch = true;
+ vm.mr.sourceBranchRemoved = false;
- Vue.nextTick(() => {
- expect(el.innerText).toContain('The source branch is being removed');
- expect(el.innerText).not.toContain('You can remove source branch now');
- expect(el.innerText).not.toContain('The source branch has been removed');
- done();
- });
+ Vue.nextTick(() => {
+ expect(vm.$el.innerText).toContain('The source branch is being removed');
+ expect(vm.$el.innerText).not.toContain('You can remove source branch now');
+ expect(vm.$el.innerText).not.toContain('The source branch has been removed');
+ done();
});
+ });
- it('should use mergedEvent updatedAt as tooltip title', () => {
- expect(
- el.querySelector('time').getAttribute('title'),
- ).toBe('mergedUpdatedAt');
- });
+ it('should use mergedEvent mergedAt as tooltip title', () => {
+ expect(
+ vm.$el.querySelector('time').getAttribute('title'),
+ ).toBe('Jan 24, 2018 1:02pm GMT+0000');
});
});
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index 3db04d99855..935d1df6dad 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -981,6 +981,16 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
end
+
+ context 'limit validation' do
+ where(:limit) do
+ [0, nil, '', 'foo']
+ end
+
+ with_them do
+ it { expect { repository.log(limit: limit) }.to raise_error(ArgumentError) }
+ end
+ end
end
describe "#rugged_commits_between" do
diff --git a/spec/lib/gitlab/popen/runner_spec.rb b/spec/lib/gitlab/popen/runner_spec.rb
new file mode 100644
index 00000000000..2e2cb4ca28f
--- /dev/null
+++ b/spec/lib/gitlab/popen/runner_spec.rb
@@ -0,0 +1,139 @@
+require 'spec_helper'
+
+describe Gitlab::Popen::Runner do
+ subject { described_class.new }
+
+ describe '#run' do
+ it 'runs the command and returns the result' do
+ run_command
+
+ expect(Gitlab::Popen).to have_received(:popen_with_detail)
+ end
+ end
+
+ describe '#all_success_and_clean?' do
+ it 'returns true when exit status is 0 and stderr is empty' do
+ run_command
+
+ expect(subject).to be_all_success_and_clean
+ end
+
+ it 'returns false when exit status is not 0' do
+ run_command(exitstatus: 1)
+
+ expect(subject).not_to be_all_success_and_clean
+ end
+
+ it 'returns false when exit stderr has something' do
+ run_command(stderr: 'stderr')
+
+ expect(subject).not_to be_all_success_and_clean
+ end
+ end
+
+ describe '#all_success?' do
+ it 'returns true when exit status is 0' do
+ run_command
+
+ expect(subject).to be_all_success
+ end
+
+ it 'returns false when exit status is not 0' do
+ run_command(exitstatus: 1)
+
+ expect(subject).not_to be_all_success
+ end
+
+ it 'returns true' do
+ run_command(stderr: 'stderr')
+
+ expect(subject).to be_all_success
+ end
+ end
+
+ describe '#all_stderr_empty?' do
+ it 'returns true when stderr is empty' do
+ run_command
+
+ expect(subject).to be_all_stderr_empty
+ end
+
+ it 'returns true when exit status is not 0' do
+ run_command(exitstatus: 1)
+
+ expect(subject).to be_all_stderr_empty
+ end
+
+ it 'returns false when exit stderr has something' do
+ run_command(stderr: 'stderr')
+
+ expect(subject).not_to be_all_stderr_empty
+ end
+ end
+
+ describe '#failed_results' do
+ it 'returns [] when everything is passed' do
+ run_command
+
+ expect(subject.failed_results).to be_empty
+ end
+
+ it 'returns the result when exit status is not 0' do
+ result = run_command(exitstatus: 1)
+
+ expect(subject.failed_results).to contain_exactly(result)
+ end
+
+ it 'returns [] when exit stderr has something' do
+ run_command(stderr: 'stderr')
+
+ expect(subject.failed_results).to be_empty
+ end
+ end
+
+ describe '#warned_results' do
+ it 'returns [] when everything is passed' do
+ run_command
+
+ expect(subject.warned_results).to be_empty
+ end
+
+ it 'returns [] when exit status is not 0' do
+ run_command(exitstatus: 1)
+
+ expect(subject.warned_results).to be_empty
+ end
+
+ it 'returns the result when exit stderr has something' do
+ result = run_command(stderr: 'stderr')
+
+ expect(subject.warned_results).to contain_exactly(result)
+ end
+ end
+
+ def run_command(
+ command: 'command',
+ stdout: 'stdout',
+ stderr: '',
+ exitstatus: 0,
+ status: double(exitstatus: exitstatus, success?: exitstatus.zero?),
+ duration: 0.1)
+
+ result =
+ Gitlab::Popen::Result.new(command, stdout, stderr, status, duration)
+
+ allow(Gitlab::Popen)
+ .to receive(:popen_with_detail)
+ .and_return(result)
+
+ subject.run([command]) do |cmd, &run|
+ expect(cmd).to eq(command)
+
+ cmd_result = run.call
+
+ expect(cmd_result).to eq(result)
+ end
+
+ subject.results.first
+ end
+end
diff --git a/spec/lib/gitlab/popen_spec.rb b/spec/lib/gitlab/popen_spec.rb
index b145ca36f26..1dbead16d5b 100644
--- a/spec/lib/gitlab/popen_spec.rb
+++ b/spec/lib/gitlab/popen_spec.rb
@@ -1,11 +1,23 @@
require 'spec_helper'
-describe 'Gitlab::Popen' do
+describe Gitlab::Popen do
let(:path) { Rails.root.join('tmp').to_s }
before do
@klass = Class.new(Object)
- @klass.send(:include, Gitlab::Popen)
+ @klass.send(:include, described_class)
+ end
+
+ describe '.popen_with_detail' do
+ subject { @klass.new.popen_with_detail(cmd) }
+
+ let(:cmd) { %W[#{Gem.ruby} -e $stdout.puts(1);$stderr.puts(2);exit(3)] }
+
+ it { expect(subject.cmd).to eq(cmd) }
+ it { expect(subject.stdout).to eq("1\n") }
+ it { expect(subject.stderr).to eq("2\n") }
+ it { expect(subject.status.exitstatus).to eq(3) }
+ it { expect(subject.duration).to be_kind_of(Numeric) }
end
context 'zero status' do
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 45a606c1ea8..f5b3b4a9fc5 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -277,7 +277,7 @@ describe Ci::Build do
allow_any_instance_of(Project).to receive(:jobs_cache_index).and_return(1)
end
- it { is_expected.to be_an(Array).and all(include(key: "key:1")) }
+ it { is_expected.to be_an(Array).and all(include(key: "key_1")) }
end
context 'when project does not have jobs_cache_index' do
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index f8a98b43e46..959383ff0b7 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -228,7 +228,7 @@ eos
it { expect(data).to be_a(Hash) }
it { expect(data[:message]).to include('adds bar folder and branch-test text file to check Repository merged_to_root_ref method') }
it { expect(data[:timestamp]).to eq('2016-09-27T14:37:46Z') }
- it { expect(data[:added]).to eq(["bar/branch-test.txt"]) }
+ it { expect(data[:added]).to contain_exactly("bar/branch-test.txt") }
it { expect(data[:modified]).to eq([]) }
it { expect(data[:removed]).to eq([]) }
end
@@ -532,8 +532,8 @@ eos
let(:commit2) { merge_request1.merge_request_diff.commits.first }
it 'returns merge_requests that introduced that commit' do
- expect(commit1.merge_requests).to eq([merge_request1, merge_request2])
- expect(commit2.merge_requests).to eq([merge_request1])
+ expect(commit1.merge_requests).to contain_exactly(merge_request1, merge_request2)
+ expect(commit2.merge_requests).to contain_exactly(merge_request1)
end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 429b6615131..eb9690df313 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1064,16 +1064,6 @@ describe MergeRequest do
end
describe '#can_be_reverted?' do
- context 'when there is no merged_at for the MR' do
- before do
- subject.metrics.update!(merged_at: nil)
- end
-
- it 'returns false' do
- expect(subject.can_be_reverted?(nil)).to be_falsey
- end
- end
-
context 'when there is no merge_commit for the MR' do
before do
subject.metrics.update!(merged_at: Time.now.utc)
@@ -1097,6 +1087,16 @@ describe MergeRequest do
end
end
+ context 'when there is no merged_at for the MR' do
+ before do
+ subject.metrics.update!(merged_at: nil)
+ end
+
+ it 'returns true' do
+ expect(subject.can_be_reverted?(nil)).to be_truthy
+ end
+ end
+
context 'when there is a revert commit' do
let(:current_user) { subject.author }
let(:branch) { subject.target_branch }
@@ -1127,6 +1127,16 @@ describe MergeRequest do
end
end
+ context 'when there is no merged_at for the MR' do
+ before do
+ subject.metrics.update!(merged_at: nil)
+ end
+
+ it 'returns false' do
+ expect(subject.can_be_reverted?(current_user)).to be_falsey
+ end
+ end
+
context 'when the revert commit is mentioned in a note just before the MR was merged' do
before do
subject.notes.last.update!(created_at: subject.metrics.merged_at - 30.seconds)
@@ -1529,7 +1539,7 @@ describe MergeRequest do
expect { subject.reload_diff }.to change { subject.merge_request_diffs.count }.by(1)
end
- it "executs diff cache service" do
+ it "executes diff cache service" do
expect_any_instance_of(MergeRequests::MergeRequestDiffCacheService).to receive(:execute).with(subject)
subject.reload_diff
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index c9b3c6cf602..1eaaadf56c5 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -3,6 +3,29 @@ require 'spec_helper'
describe JiraService do
include Gitlab::Routing
+ describe '#options' do
+ let(:service) do
+ described_class.new(
+ project: build_stubbed(:project),
+ active: true,
+ username: 'username',
+ password: 'test',
+ jira_issue_transition_id: 24,
+ url: 'http://jira.test.com/path/'
+ )
+ end
+
+ it 'sets the URL properly' do
+ # jira-ruby gem parses the URI and handles trailing slashes
+ # fine: https://github.com/sumoheavy/jira-ruby/blob/v1.4.1/lib/jira/http_client.rb#L59
+ expect(service.options[:site]).to eq('http://jira.test.com/')
+ end
+
+ it 'leaves out trailing slashes in context' do
+ expect(service.options[:context_path]).to eq('/path')
+ end
+ end
+
describe "Associations" do
it { is_expected.to belong_to :project }
it { is_expected.to have_one :service_hook }
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 7c61c6b7299..d4070b320ed 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -222,20 +222,20 @@ describe Repository do
it 'sets follow when path is a single path' do
expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: true)).and_call_original.twice
- repository.commits('master', path: 'README.md')
- repository.commits('master', path: ['README.md'])
+ repository.commits('master', limit: 1, path: 'README.md')
+ repository.commits('master', limit: 1, path: ['README.md'])
end
it 'does not set follow when path is multiple paths' do
expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: false)).and_call_original
- repository.commits('master', path: ['README.md', 'CHANGELOG'])
+ repository.commits('master', limit: 1, path: ['README.md', 'CHANGELOG'])
end
it 'does not set follow when there are no paths' do
expect(Gitlab::Git::Commit).to receive(:where).with(a_hash_including(follow: false)).and_call_original
- repository.commits('master')
+ repository.commits('master', limit: 1)
end
end
@@ -455,7 +455,7 @@ describe Repository do
expect do
repository.create_dir(user, 'newdir',
message: 'Create newdir', branch_name: 'master')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
newdir = repository.tree('master', 'newdir')
expect(newdir.path).to eq('newdir')
@@ -469,7 +469,7 @@ describe Repository do
repository.create_dir(user, 'newdir',
message: 'Create newdir', branch_name: 'patch',
start_branch_name: 'master', start_project: forked_project)
- end.to change { repository.commits('master').count }.by(0)
+ end.to change { repository.count_commits(ref: 'master') }.by(0)
expect(repository.branch_exists?('patch')).to be_truthy
expect(forked_project.repository.branch_exists?('patch')).to be_falsy
@@ -486,7 +486,7 @@ describe Repository do
message: 'Add newdir',
branch_name: 'master',
author_email: author_email, author_name: author_name)
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
last_commit = repository.commit
@@ -502,7 +502,7 @@ describe Repository do
repository.create_file(user, 'NEWCHANGELOG', 'Changelog!',
message: 'Create changelog',
branch_name: 'master')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
blob = repository.blob_at('master', 'NEWCHANGELOG')
@@ -514,7 +514,7 @@ describe Repository do
repository.create_file(user, 'new_dir/new_file.txt', 'File!',
message: 'Create new_file with new_dir',
branch_name: 'master')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
expect(repository.tree('master', 'new_dir').path).to eq('new_dir')
expect(repository.blob_at('master', 'new_dir/new_file.txt').data).to eq('File!')
@@ -538,7 +538,7 @@ describe Repository do
branch_name: 'master',
author_email: author_email,
author_name: author_name)
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
last_commit = repository.commit
@@ -554,7 +554,7 @@ describe Repository do
repository.update_file(user, 'CHANGELOG', 'Changelog!',
message: 'Update changelog',
branch_name: 'master')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
blob = repository.blob_at('master', 'CHANGELOG')
@@ -567,7 +567,7 @@ describe Repository do
branch_name: 'master',
previous_path: 'LICENSE',
message: 'Changes filename')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
files = repository.ls_files('master')
@@ -584,7 +584,7 @@ describe Repository do
message: 'Update README',
author_email: author_email,
author_name: author_name)
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
last_commit = repository.commit
@@ -599,7 +599,7 @@ describe Repository do
expect do
repository.delete_file(user, 'README',
message: 'Remove README', branch_name: 'master')
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
expect(repository.blob_at('master', 'README')).to be_nil
end
@@ -610,7 +610,7 @@ describe Repository do
repository.delete_file(user, 'README',
message: 'Remove README', branch_name: 'master',
author_email: author_email, author_name: author_name)
- end.to change { repository.commits('master').count }.by(1)
+ end.to change { repository.count_commits(ref: 'master') }.by(1)
last_commit = repository.commit
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index 34db50dc082..ff5f207487b 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -62,7 +62,7 @@ describe API::Commits do
context "since optional parameter" do
it "returns project commits since provided parameter" do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 2)
after = commits.second.created_at
get api("/projects/#{project_id}/repository/commits?since=#{after.utc.iso8601}", user)
@@ -73,7 +73,7 @@ describe API::Commits do
end
it 'include correct pagination headers' do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 2)
after = commits.second.created_at
commit_count = project.repository.count_commits(ref: 'master', after: after).to_s
@@ -87,12 +87,12 @@ describe API::Commits do
context "until optional parameter" do
it "returns project commits until provided parameter" do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 20)
before = commits.second.created_at
get api("/projects/#{project_id}/repository/commits?until=#{before.utc.iso8601}", user)
- if commits.size >= 20
+ if commits.size == 20
expect(json_response.size).to eq(20)
else
expect(json_response.size).to eq(commits.size - 1)
@@ -103,7 +103,7 @@ describe API::Commits do
end
it 'include correct pagination headers' do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 2)
before = commits.second.created_at
commit_count = project.repository.count_commits(ref: 'master', before: before).to_s
@@ -181,7 +181,7 @@ describe API::Commits do
let(:page) { 3 }
it 'returns the third 5 commits' do
- commit = project.repository.commits('HEAD', offset: (page - 1) * per_page).first
+ commit = project.repository.commits('HEAD', limit: per_page, offset: (page - 1) * per_page).first
expect(json_response.size).to eq(per_page)
expect(json_response.first['id']).to eq(commit.id)
diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb
index 8e2982f1a5d..14dd9da119d 100644
--- a/spec/requests/api/merge_requests_spec.rb
+++ b/spec/requests/api/merge_requests_spec.rb
@@ -198,6 +198,8 @@ describe API::MergeRequests do
create(:merge_request, state: 'closed', milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time)
+ create(:merge_request, milestone: milestone1, author: user, assignee: user, source_project: project, target_project: project, title: "Test", created_at: base_time)
+
expect do
get api("/projects/#{project.id}/merge_requests", user)
end.not_to exceed_query_limit(control)
diff --git a/spec/requests/api/v3/commits_spec.rb b/spec/requests/api/v3/commits_spec.rb
index 34c543bffe8..9ef3b859001 100644
--- a/spec/requests/api/v3/commits_spec.rb
+++ b/spec/requests/api/v3/commits_spec.rb
@@ -36,7 +36,7 @@ describe API::V3::Commits do
context "since optional parameter" do
it "returns project commits since provided parameter" do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 2)
since = commits.second.created_at
get v3_api("/projects/#{project.id}/repository/commits?since=#{since.utc.iso8601}", user)
@@ -49,12 +49,12 @@ describe API::V3::Commits do
context "until optional parameter" do
it "returns project commits until provided parameter" do
- commits = project.repository.commits("master")
+ commits = project.repository.commits("master", limit: 20)
before = commits.second.created_at
get v3_api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user)
- if commits.size >= 20
+ if commits.size == 20
expect(json_response.size).to eq(20)
else
expect(json_response.size).to eq(commits.size - 1)
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index 7a01d3dd698..7c3374c6113 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -55,11 +55,12 @@ describe MergeRequests::RefreshService do
before do
allow(refresh_service).to receive(:execute_hooks)
- refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
- reload_mrs
end
it 'executes hooks with update action' do
+ refresh_service.execute(@oldrev, @newrev, 'refs/heads/master')
+ reload_mrs
+
expect(refresh_service).to have_received(:execute_hooks)
.with(@merge_request, 'update', old_rev: @oldrev)
@@ -72,6 +73,26 @@ describe MergeRequests::RefreshService do
expect(@build_failed_todo).to be_done
expect(@fork_build_failed_todo).to be_done
end
+
+ context 'when source branch ref does not exists' do
+ before do
+ DeleteBranchService.new(@project, @user).execute(@merge_request.source_branch)
+ end
+
+ it 'closes MRs without source branch ref' do
+ expect { refresh_service.execute(@oldrev, @newrev, 'refs/heads/master') }
+ .to change { @merge_request.reload.state }
+ .from('opened')
+ .to('closed')
+
+ expect(@fork_merge_request.reload).to be_open
+ end
+
+ it 'does not change the merge request diff' do
+ expect { refresh_service.execute(@oldrev, @newrev, 'refs/heads/master') }
+ .not_to change { @merge_request.reload.merge_request_diff }
+ end
+ end
end
context 'when pipeline exists for the source branch' do
diff --git a/spec/support/javascript_fixtures_helpers.rb b/spec/support/javascript_fixtures_helpers.rb
index 923c8080e6c..2197bc9d853 100644
--- a/spec/support/javascript_fixtures_helpers.rb
+++ b/spec/support/javascript_fixtures_helpers.rb
@@ -1,6 +1,5 @@
require 'action_dispatch/testing/test_request'
require 'fileutils'
-require 'gitlab/popen'
module JavaScriptFixturesHelpers
include Gitlab::Popen
diff --git a/spec/tasks/gitlab/gitaly_rake_spec.rb b/spec/tasks/gitlab/gitaly_rake_spec.rb
index 6aba86fdc3c..b37d6ac831f 100644
--- a/spec/tasks/gitlab/gitaly_rake_spec.rb
+++ b/spec/tasks/gitlab/gitaly_rake_spec.rb
@@ -76,7 +76,11 @@ describe 'gitlab:gitaly namespace rake task' do
end
context 'when Rails.env is test' do
- let(:command) { %w[make BUNDLE_FLAGS=--no-deployment] }
+ let(:command) do
+ %W[make
+ BUNDLE_FLAGS=--no-deployment
+ BUNDLE_PATH=#{Bundler.bundle_path}]
+ end
before do
allow(Rails.env).to receive(:test?).and_return(true)
diff --git a/spec/tasks/gitlab/task_helpers_spec.rb b/spec/tasks/gitlab/task_helpers_spec.rb
index fae5ec35c47..e9322ec4931 100644
--- a/spec/tasks/gitlab/task_helpers_spec.rb
+++ b/spec/tasks/gitlab/task_helpers_spec.rb
@@ -1,5 +1,4 @@
require 'spec_helper'
-require 'tasks/gitlab/task_helpers'
class TestHelpersTest
include Gitlab::TaskHelpers