summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md68
-rw-r--r--Dangerfile2
-rw-r--r--GITLAB_WORKHORSE_VERSION2
-rw-r--r--Gemfile4
-rw-r--r--Gemfile.lock10
-rw-r--r--app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js2
-rw-r--r--app/assets/javascripts/gl_form.js4
-rw-r--r--app/assets/javascripts/ide/constants.js7
-rw-r--r--app/assets/javascripts/ide/stores/utils.js10
-rw-r--r--app/assets/javascripts/issuable_form.js2
-rw-r--r--app/assets/javascripts/mr_popover/index.js15
-rw-r--r--app/assets/javascripts/pages/profiles/show/index.js2
-rw-r--r--app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue11
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js13
-rw-r--r--app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue22
-rw-r--r--app/assets/stylesheets/components/related_items_list.scss97
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb7
-rw-r--r--app/controllers/concerns/milestone_actions.rb2
-rw-r--r--app/controllers/projects/branches_controller.rb8
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb2
-rw-r--r--app/controllers/sessions_controller.rb12
-rw-r--r--app/helpers/labels_helper.rb48
-rw-r--r--app/helpers/markup_helper.rb3
-rw-r--r--app/models/ci/pipeline.rb1
-rw-r--r--app/models/concerns/cache_markdown_field.rb2
-rw-r--r--app/models/global_label.rb6
-rw-r--r--app/models/global_milestone.rb4
-rw-r--r--app/models/internal_id.rb8
-rw-r--r--app/models/label.rb5
-rw-r--r--app/presenters/label_presenter.rb43
-rw-r--r--app/services/delete_branch_service.rb24
-rw-r--r--app/services/service_response.rb31
-rw-r--r--app/views/admin/labels/_label.html.haml2
-rw-r--r--app/views/groups/labels/index.html.haml7
-rw-r--r--app/views/import/gitea/new.html.haml6
-rw-r--r--app/views/import/gitea/status.html.haml2
-rw-r--r--app/views/projects/_import_project_pane.html.haml2
-rw-r--r--app/views/projects/issues/_issue.html.haml4
-rw-r--r--app/views/projects/issues/show.html.haml2
-rw-r--r--app/views/projects/labels/index.html.haml4
-rw-r--r--app/views/projects/merge_requests/_merge_request.html.haml4
-rw-r--r--app/views/shared/_delete_label_modal.html.haml4
-rw-r--r--app/views/shared/_label.html.haml8
-rw-r--r--app/views/shared/_label_row.html.haml5
-rw-r--r--app/views/shared/_sidebar_toggle_button.html.haml4
-rw-r--r--app/views/shared/icons/_gitea_logo.svg.erb1
-rw-r--r--app/views/shared/issuable/_sidebar.html.haml2
-rw-r--r--app/views/shared/milestones/_issuable.html.haml2
-rw-r--r--changelogs/unreleased/27424-tklk-gitea-logo.yml5
-rw-r--r--changelogs/unreleased/54656-500-error-on-save-of-general-pipeline-settings-timeout.yml5
-rw-r--r--changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml5
-rw-r--r--changelogs/unreleased/60540-merge-request-popover-is-not-working-on-the-to-do-page.yml5
-rw-r--r--changelogs/unreleased/60569-timeline-entry-label-link-is-not-applying-the-filter-on-issues.yml5
-rw-r--r--changelogs/unreleased/add-ci-variable-protected-ref.yml5
-rw-r--r--changelogs/unreleased/ce-remove-already-signed-in.yml5
-rw-r--r--changelogs/unreleased/fj-60827-fix-web-strategy-error.yml5
-rw-r--r--changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml5
-rw-r--r--changelogs/unreleased/sh-bump-ruby-required-version-check.yml5
-rw-r--r--changelogs/unreleased/sh-disable-internal-ids-available-check.yml5
-rw-r--r--changelogs/unreleased/sh-upgrade-grpc-and-protobuf.yml5
-rw-r--r--changelogs/unreleased/update-workhorse-master.yml5
-rw-r--r--danger/ce_ee_vue_templates/Dangerfile56
-rw-r--r--db/fixtures/development/09_issues.rb20
-rw-r--r--doc/ci/variables/predefined_variables.md1
-rw-r--r--doc/development/contributing/issue_workflow.md5
-rw-r--r--doc/development/rake_tasks.md16
-rw-r--r--doc/user/project/integrations/gitlab_slack_application.md65
-rw-r--r--doc/user/project/integrations/img/gitlab_slack_app_landing_page.pngbin0 -> 32992 bytes
-rw-r--r--doc/user/project/integrations/project_services.md6
-rw-r--r--doc/user/project/integrations/slack_slash_commands.md13
-rw-r--r--doc/user/project/pipelines/job_artifacts.md3
-rw-r--r--doc/user/project/repository/index.md33
-rw-r--r--lib/api/branches.rb4
-rw-r--r--lib/banzai/filter/label_reference_filter.rb6
-rw-r--r--lib/gitlab/graphql/authorize/authorize_field_service.rb6
-rw-r--r--lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb5
-rw-r--r--lib/quality/seeders/issues.rb58
-rw-r--r--lib/tasks/gitlab/seed.rake34
-rw-r--r--package.json2
-rw-r--r--spec/controllers/omniauth_callbacks_controller_spec.rb6
-rw-r--r--spec/controllers/projects/settings/ci_cd_controller_spec.rb9
-rw-r--r--spec/features/dashboard/todos/todos_spec.rb20
-rw-r--r--spec/features/users/login_spec.rb16
-rw-r--r--spec/frontend/gfm_auto_complete_spec.js2
-rw-r--r--spec/frontend/mr_popover/index_spec.js20
-rw-r--r--spec/helpers/labels_helper_spec.rb48
-rw-r--r--spec/javascripts/ide/stores/modules/commit/actions_spec.js5
-rw-r--r--spec/javascripts/ide/stores/utils_spec.js19
-rw-r--r--spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb16
-rw-r--r--spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb12
-rw-r--r--spec/lib/quality/seeders/issues_spec.rb18
-rw-r--r--spec/models/ci/bridge_spec.rb2
-rw-r--r--spec/models/ci/build_spec.rb3
-rw-r--r--spec/models/ci/pipeline_spec.rb3
-rw-r--r--spec/models/internal_id_spec.rb11
-rw-r--r--spec/presenters/label_presenter_spec.rb65
-rw-r--r--spec/services/delete_branch_service_spec.rb24
-rw-r--r--spec/services/service_response_spec.rb57
-rw-r--r--spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb6
-rw-r--r--spec/support/helpers/login_helpers.rb3
-rw-r--r--yarn.lock8
102 files changed, 943 insertions, 366 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3d9b6359f6b..bd4c0e479cc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,26 @@
documentation](doc/development/changelog.md) for instructions on adding your own
entry.
+## 11.10.1 (2019-04-23)
+
+### Fixed (2 changes)
+
+- Upgrade Gitaly to 1.34.0. !27494
+- Fix filtering of labels from system note link. !27507
+
+### Changed (1 change)
+
+- Disable just-in-time Kubernetes resource creation for project level clusters. !27352
+
+### Performance (1 change)
+
+- Bring back Rugged implementation of ListCommitsByOid. !27441
+
+### Other (1 change)
+
+- Bump required Ruby version check to 2.5.3. !27495
+
+
## 11.10.0 (2019-04-22)
### Security (9 changes)
@@ -233,6 +253,13 @@ entry.
- Removes EE differences for environment_item.vue.
+## 11.9.9 (2019-04-23)
+
+### Performance (1 change)
+
+- Bring back Rugged implementation of ListCommitsByOid. !27441
+
+
## 11.9.8 (2019-04-11)
### Deprecated (1 change)
@@ -584,6 +611,28 @@ entry.
- Removes EE differences for jobs/getters.js.
+## 11.8.8 (2019-04-23)
+
+### Fixed (5 changes)
+
+- Bring back Rugged implementation of find_commit. !25477
+- Fix bug in BitBucket imports with SHA shorter than 40 chars. !26050
+- Fix health checks not working behind load balancers. !26055
+- Fix error creating a merge request when diff includes a null byte. !26190
+- Avoid excessive recursive calls with Rugged TreeEntries. !26813
+
+### Performance (1 change)
+
+- Bring back Rugged implementation of ListCommitsByOid. !27441
+
+### Other (4 changes)
+
+- Bring back Rugged implementation of GetTreeEntries. !25674
+- Bring back Rugged implementation of CommitIsAncestor. !25702
+- Bring back Rugged implementation of TreeEntry. !25706
+- Bring back Rugged implementation of commit_tree_entry. !25896
+
+
## 11.8.3 (2019-03-19)
### Security (1 change)
@@ -853,6 +902,25 @@ entry.
- Creates mixin to reduce code duplication between CE and EE in graph component.
+## 11.7.12 (2019-04-23)
+
+### Fixed (2 changes)
+
+- Bring back Rugged implementation of find_commit. !25477
+- Avoid excessive recursive calls with Rugged TreeEntries. !26813
+
+### Performance (1 change)
+
+- Bring back Rugged implementation of ListCommitsByOid. !27441
+
+### Other (4 changes)
+
+- Bring back Rugged implementation of GetTreeEntries. !25674
+- Bring back Rugged implementation of CommitIsAncestor. !25702
+- Bring back Rugged implementation of TreeEntry. !25706
+- Bring back Rugged implementation of commit_tree_entry. !25896
+
+
## 11.7.11 (2019-04-09)
- No changes.
diff --git a/Dangerfile b/Dangerfile
index 3e8cb456003..9e3a08949b0 100644
--- a/Dangerfile
+++ b/Dangerfile
@@ -1,3 +1,4 @@
+# frozen_string_literal: true
danger.import_plugin('danger/plugins/helper.rb')
unless helper.release_automation?
@@ -16,4 +17,5 @@ unless helper.release_automation?
danger.import_dangerfile(path: 'danger/roulette')
danger.import_dangerfile(path: 'danger/single_codebase')
danger.import_dangerfile(path: 'danger/gitlab_ui_wg')
+ danger.import_dangerfile(path: 'danger/ce_ee_vue_templates')
end
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index acd405b1d62..df5119ec64e 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-8.6.0
+8.7.0
diff --git a/Gemfile b/Gemfile
index 25b7376f424..65ba7137892 100644
--- a/Gemfile
+++ b/Gemfile
@@ -418,9 +418,9 @@ end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 1.19.0', require: 'gitaly'
-gem 'grpc', '~> 1.15.0'
+gem 'grpc', '~> 1.19.0'
-gem 'google-protobuf', '~> 3.6'
+gem 'google-protobuf', '~> 3.7.1'
gem 'toml-rb', '~> 1.0.0', require: false
diff --git a/Gemfile.lock b/Gemfile.lock
index 15ed261526a..da8f8db9528 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -316,8 +316,8 @@ GEM
mime-types (~> 3.0)
representable (~> 3.0)
retriable (>= 2.0, < 4.0)
- google-protobuf (3.6.1)
- googleapis-common-protos-types (1.0.3)
+ google-protobuf (3.7.1)
+ googleapis-common-protos-types (1.0.4)
google-protobuf (~> 3.0)
googleauth (0.6.6)
faraday (~> 0.12)
@@ -348,7 +348,7 @@ GEM
railties
sprockets-rails
graphql (1.8.1)
- grpc (1.15.0)
+ grpc (1.19.0)
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
haml (5.0.4)
@@ -1062,7 +1062,7 @@ DEPENDENCIES
gitlab_omniauth-ldap (~> 2.1.1)
gon (~> 6.2)
google-api-client (~> 0.23)
- google-protobuf (~> 3.6)
+ google-protobuf (~> 3.7.1)
gpgme (~> 2.0.18)
grape (~> 1.1.0)
grape-entity (~> 0.7.1)
@@ -1070,7 +1070,7 @@ DEPENDENCIES
grape_logging (~> 1.7)
graphiql-rails (~> 1.4.10)
graphql (~> 1.8.0)
- grpc (~> 1.15.0)
+ grpc (~> 1.19.0)
haml_lint (~> 0.28.0)
hamlit (~> 2.8.8)
hangouts-chat (~> 0.0.5)
diff --git a/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js b/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js
index 55c68139ded..b7200150925 100644
--- a/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js
+++ b/app/assets/javascripts/behaviors/markdown/gfm_auto_complete.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import { parseBoolean } from '~/lib/utils/common_utils';
-import GfmAutoComplete from '~/gfm_auto_complete';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
export default function initGFMInput() {
$('.js-gfm-input:not(.js-vue-textarea)').each((i, el) => {
diff --git a/app/assets/javascripts/gl_form.js b/app/assets/javascripts/gl_form.js
index f5e2e46237f..5a6d44ef838 100644
--- a/app/assets/javascripts/gl_form.js
+++ b/app/assets/javascripts/gl_form.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import autosize from 'autosize';
-import GfmAutoComplete, * as GFMConfig from './gfm_auto_complete';
+import GfmAutoComplete, { defaultAutocompleteConfig } from 'ee_else_ce/gfm_auto_complete';
import dropzoneInput from './dropzone_input';
import { addMarkdownListeners, removeMarkdownListeners } from './lib/utils/text_markdown';
@@ -8,7 +8,7 @@ export default class GLForm {
constructor(form, enableGFM = {}) {
this.form = form;
this.textarea = this.form.find('textarea.js-gfm-input');
- this.enableGFM = Object.assign({}, GFMConfig.defaultAutocompleteConfig, enableGFM);
+ this.enableGFM = Object.assign({}, defaultAutocompleteConfig, enableGFM);
// Disable autocomplete for keywords which do not have dataSources available
const dataSources = (gl.GfmAutoComplete && gl.GfmAutoComplete.dataSources) || {};
Object.keys(this.enableGFM).forEach(item => {
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index 7c560c89695..e30670e119f 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -72,4 +72,11 @@ export const modalTypes = {
tree: 'tree',
};
+export const commitActionTypes = {
+ move: 'move',
+ delete: 'delete',
+ create: 'create',
+ update: 'update',
+};
+
export const packageJsonPath = 'package.json';
diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js
index 66c5180b782..bcc9ca60d9b 100644
--- a/app/assets/javascripts/ide/stores/utils.js
+++ b/app/assets/javascripts/ide/stores/utils.js
@@ -1,3 +1,5 @@
+import { commitActionTypes } from '../constants';
+
export const dataStructure = () => ({
id: '',
// Key will contain a mixture of ID and path
@@ -114,14 +116,14 @@ export const setPageTitle = title => {
export const commitActionForFile = file => {
if (file.prevPath) {
- return 'move';
+ return commitActionTypes.move;
} else if (file.deleted) {
- return 'delete';
+ return commitActionTypes.delete;
} else if (file.tempFile) {
- return 'create';
+ return commitActionTypes.create;
}
- return 'update';
+ return commitActionTypes.update;
};
export const getCommitFiles = stagedFiles =>
diff --git a/app/assets/javascripts/issuable_form.js b/app/assets/javascripts/issuable_form.js
index 9336b71cfd7..7576d36f27d 100644
--- a/app/assets/javascripts/issuable_form.js
+++ b/app/assets/javascripts/issuable_form.js
@@ -1,8 +1,8 @@
import $ from 'jquery';
import Pikaday from 'pikaday';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import Autosave from './autosave';
import UsersSelect from './users_select';
-import GfmAutoComplete from './gfm_auto_complete';
import ZenMode from './zen_mode';
import AutoWidthDropdownSelect from './issuable/auto_width_dropdown_select';
import { parsePikadayDate, pikadayToString } from './lib/utils/datetime_utility';
diff --git a/app/assets/javascripts/mr_popover/index.js b/app/assets/javascripts/mr_popover/index.js
index 9a97e98f9db..18c0e201300 100644
--- a/app/assets/javascripts/mr_popover/index.js
+++ b/app/assets/javascripts/mr_popover/index.js
@@ -22,13 +22,10 @@ const handleUserPopoverMouseOut = ({ target }) => {
* Adds a MergeRequestPopover component to the body, hands over as much data as the target element has in data attributes.
* loads based on data-project-path and data-iid more data about an MR from the API and sets it on the popover
*/
-const handleMRPopoverMount = apolloProvider => ({ target }) => {
+const handleMRPopoverMount = ({ apolloProvider, projectPath, mrTitle, iid }) => ({ target }) => {
// Add listener to actually remove it again
target.addEventListener('mouseleave', handleUserPopoverMouseOut);
- const { projectPath, mrTitle, iid } = target.dataset;
- const mergeRequest = {};
-
renderFn = setTimeout(() => {
const MRPopoverComponent = Vue.extend(MRPopover);
renderedPopover = new MRPopoverComponent({
@@ -36,7 +33,6 @@ const handleMRPopoverMount = apolloProvider => ({ target }) => {
target,
projectPath,
mergeRequestIID: iid,
- mergeRequest,
mergeRequestTitle: mrTitle,
},
apolloProvider,
@@ -57,8 +53,13 @@ export default elements => {
const listenerAddedAttr = 'data-mr-listener-added';
mrLinks.forEach(el => {
- if (!el.getAttribute(listenerAddedAttr)) {
- el.addEventListener('mouseenter', handleMRPopoverMount(apolloProvider));
+ const { projectPath, mrTitle, iid } = el.dataset;
+
+ if (!el.getAttribute(listenerAddedAttr) && projectPath && mrTitle && iid) {
+ el.addEventListener(
+ 'mouseenter',
+ handleMRPopoverMount({ apolloProvider, projectPath, mrTitle, iid }),
+ );
el.setAttribute(listenerAddedAttr, true);
}
});
diff --git a/app/assets/javascripts/pages/profiles/show/index.js b/app/assets/javascripts/pages/profiles/show/index.js
index 0dd0d5336fc..e726ab0e220 100644
--- a/app/assets/javascripts/pages/profiles/show/index.js
+++ b/app/assets/javascripts/pages/profiles/show/index.js
@@ -1,6 +1,6 @@
import $ from 'jquery';
import createFlash from '~/flash';
-import GfmAutoComplete from '~/gfm_auto_complete';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import emojiRegex from 'emoji-regex';
import EmojiMenu from './emoji_menu';
diff --git a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
index 7f86741ed29..c0659a0173a 100644
--- a/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
+++ b/app/assets/javascripts/set_status_modal/set_status_modal_wrapper.vue
@@ -2,7 +2,7 @@
import $ from 'jquery';
import createFlash from '~/flash';
import Icon from '~/vue_shared/components/icon.vue';
-import GfmAutoComplete from '~/gfm_auto_complete';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import { __, s__ } from '~/locale';
import Api from '~/api';
import { GlModal, GlTooltipDirective } from '@gitlab/ui';
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
index bb76eb1030d..851939d5d4e 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue
@@ -3,6 +3,7 @@ import successSvg from 'icons/_icon_status_success.svg';
import warningSvg from 'icons/_icon_status_warning.svg';
import simplePoll from '~/lib/utils/simple_poll';
import { __ } from '~/locale';
+import readyToMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/ready_to_merge';
import MergeRequest from '../../../merge_request';
import Flash from '../../../flash';
import statusIcon from '../mr_widget_status_icon.vue';
@@ -21,6 +22,7 @@ export default {
CommitEdit,
CommitMessageDropdown,
},
+ mixins: [readyToMergeMixin],
props: {
mr: { type: Object, required: true },
service: { type: Object, required: true },
@@ -94,15 +96,6 @@ export default {
shouldShowMergeOptionsDropdown() {
return this.mr.isPipelineActive && !this.mr.onlyAllowMergeIfPipelineSucceeds;
},
- isMergeButtonDisabled() {
- const { commitMessage } = this;
- return Boolean(
- !commitMessage.length ||
- !this.shouldShowMergeControls ||
- this.isMakingRequest ||
- this.mr.preventMerge,
- );
- },
isRemoveSourceBranchButtonDisabled() {
return this.isMergeButtonDisabled;
},
diff --git a/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js b/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js
new file mode 100644
index 00000000000..b2e64506472
--- /dev/null
+++ b/app/assets/javascripts/vue_merge_request_widget/mixins/ready_to_merge.js
@@ -0,0 +1,13 @@
+export default {
+ computed: {
+ isMergeButtonDisabled() {
+ const { commitMessage } = this;
+ return Boolean(
+ !commitMessage.length ||
+ !this.shouldShowMergeControls ||
+ this.isMakingRequest ||
+ this.mr.preventMerge,
+ );
+ },
+ },
+};
diff --git a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
index bf96ce0bafb..ffde55bf083 100644
--- a/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
+++ b/app/assets/javascripts/vue_shared/components/issue/related_issuable_item.vue
@@ -45,10 +45,10 @@ export default {
'issuable-info-container': !canReorder,
'card-body': canReorder,
}"
- class="item-body"
+ class="item-body d-flex align-items-center p-2 p-lg-3 p-xl-2 pl-xl-3"
>
- <div class="item-contents">
- <div class="item-title d-flex align-items-center">
+ <div class="item-contents d-flex align-items-center flex-wrap flex-grow-1 flex-xl-nowrap">
+ <div class="item-title d-flex align-items-center mb-1 mb-xl-0">
<icon
v-if="hasState"
v-tooltip
@@ -65,13 +65,15 @@ export default {
name="eye-slash"
:size="16"
:title="__('Confidential')"
- class="confidential-icon append-right-4"
+ class="confidential-icon append-right-4 align-self-baseline align-self-md-auto mt-xl-0"
:aria-label="__('Confidential')"
/>
<a :href="computedPath" class="sortable-link">{{ title }}</a>
</div>
- <div class="item-meta">
- <div class="d-flex align-items-center item-path-id">
+ <div class="item-meta d-flex flex-wrap mt-xl-0 justify-content-xl-end flex-xl-nowrap">
+ <div
+ class="d-flex align-items-center item-path-id order-md-0 mt-md-0 mt-1 ml-xl-2 mr-xl-auto"
+ >
<icon
v-if="hasState"
v-tooltip
@@ -88,7 +90,9 @@ export default {
}}</span>
{{ pathIdSeparator }}{{ itemId }}
</div>
- <div class="item-meta-child d-flex align-items-center">
+ <div
+ class="item-meta-child d-flex align-items-center order-0 flex-wrap mr-md-1 ml-md-auto ml-xl-2 flex-xl-nowrap"
+ >
<span v-if="hasPipeline" class="mr-ci-status pr-2">
<a :href="pipelineStatus.details_path">
<ci-icon v-gl-tooltip :status="pipelineStatus" :title="pipelineStatusTooltip" />
@@ -105,7 +109,7 @@ export default {
<issue-assignees
v-if="assignees.length"
:assignees="assignees"
- class="item-assignees d-inline-flex"
+ class="item-assignees d-inline-flex align-items-center align-self-end ml-auto ml-md-0 mb-md-0 order-2 flex-xl-grow-0 mt-xl-0 mr-xl-1"
/>
</div>
</div>
@@ -115,7 +119,7 @@ export default {
v-tooltip
:disabled="removeDisabled"
type="button"
- class="btn btn-default btn-svg btn-item-remove js-issue-item-remove-button qa-remove-issue-button"
+ class="btn btn-default btn-svg btn-item-remove js-issue-item-remove-button qa-remove-issue-button mr-xl-0 align-self-xl-center"
title="Remove"
aria-label="Remove"
@click="onRemoveRequest"
diff --git a/app/assets/stylesheets/components/related_items_list.scss b/app/assets/stylesheets/components/related_items_list.scss
index 0fdb11d311a..7f9cf1266b1 100644
--- a/app/assets/stylesheets/components/related_items_list.scss
+++ b/app/assets/stylesheets/components/related_items_list.scss
@@ -16,10 +16,7 @@ $item-weight-max-width: 48px;
}
.item-body {
- display: flex;
position: relative;
- align-items: center;
- padding: $gl-padding-8;
line-height: $gl-line-height;
.issue-token-state-icon-open {
@@ -49,14 +46,11 @@ $item-weight-max-width: 48px;
}
.confidential-icon {
- align-self: baseline;
color: $orange-600;
- margin-right: $gl-padding-4;
}
.item-title {
flex-basis: 100%;
- margin-bottom: $gl-padding-8;
font-size: $gl-font-size-small;
&.mr-title {
@@ -80,24 +74,12 @@ $item-weight-max-width: 48px;
}
}
-.item-contents {
- display: flex;
- align-items: center;
- flex-wrap: wrap;
- flex-grow: 1;
-}
-
.item-meta {
- display: flex;
- flex-wrap: wrap;
flex-basis: 100%;
font-size: $gl-font-size-small;
color: $gl-text-color-secondary;
.item-meta-child {
- order: 0;
- display: flex;
- flex-wrap: wrap;
flex-basis: 100%;
}
@@ -117,16 +99,10 @@ $item-weight-max-width: 48px;
}
.item-weight {
- margin-right: 0;
max-width: $item-weight-max-width;
}
.item-assignees {
- order: 2;
- align-self: flex-end;
- align-items: center;
- margin-left: auto;
-
.user-avatar-link {
margin-right: -$gl-padding-4;
@@ -162,7 +138,6 @@ $item-weight-max-width: 48px;
}
.item-path-id {
- margin-top: $gl-padding-4;
font-size: $gl-font-size-xs;
white-space: nowrap;
@@ -176,8 +151,10 @@ $item-weight-max-width: 48px;
display: block;
}
- &:not(.mr-item-path) {
- order: 1;
+ @include media-breakpoint-down(sm) {
+ &:not(.mr-item-path) {
+ order: 1;
+ }
}
}
@@ -219,15 +196,14 @@ $item-weight-max-width: 48px;
.item-body {
.item-contents {
min-width: 0;
+ }
- .item-title {
- flex-basis: unset;
- // 95% because we compensate
- // for remove button which is
- // positioned absolutely
- width: 95%;
- margin-bottom: $gl-padding-4;
- }
+ .item-title {
+ flex-basis: unset;
+ // 95% because we compensate
+ // for remove button which is
+ // positioned absolutely
+ width: 95%;
}
.btn-item-remove {
@@ -236,34 +212,19 @@ $item-weight-max-width: 48px;
}
.item-meta {
- .item-path-id {
- order: 0;
- margin-top: 0;
- }
-
.item-meta-child {
flex-basis: unset;
- margin-left: auto;
- margin-right: $gl-padding-4;
~ .item-assignees {
margin-left: $gl-padding-4;
}
}
-
- .item-assignees {
- margin-bottom: 0;
- margin-left: 0;
- order: 2;
- }
}
}
/* Medium devices (desktops, 992px and up) */
@include media-breakpoint-up(lg) {
.item-body {
- padding: $gl-padding;
-
.item-title {
font-size: $gl-font-size;
}
@@ -277,12 +238,7 @@ $item-weight-max-width: 48px;
/* Large devices (large desktops, 1200px and up) */
@include media-breakpoint-up(xl) {
.item-body {
- padding: $gl-padding-8;
- padding-left: $gl-padding;
-
.item-title {
- display: flex;
- margin-bottom: 0;
min-width: 0;
width: auto;
flex-basis: unset;
@@ -293,43 +249,18 @@ $item-weight-max-width: 48px;
display: block;
margin-right: $gl-padding-8;
}
-
- .confidential-icon {
- align-self: auto;
- margin-top: 0;
- }
}
}
.item-contents {
- flex-wrap: nowrap;
overflow: hidden;
}
.item-meta {
- margin-top: 0;
- justify-content: flex-end;
flex: 1;
- flex-wrap: nowrap;
-
- .item-meta-child {
- margin-left: $gl-padding-8;
- flex-wrap: nowrap;
- }
- }
-
- .item-path-id {
- order: 0;
- margin-top: 0;
- margin-left: $gl-padding-8;
- margin-right: auto;
}
.item-assignees {
- flex-grow: 0;
- margin-top: 0;
- margin-right: $gl-padding-4;
-
.avatar {
height: $gl-padding-24;
width: $gl-padding-24;
@@ -345,10 +276,8 @@ $item-weight-max-width: 48px;
.btn-item-remove {
position: relative;
- align-self: center;
top: initial;
right: 0;
- margin-right: 0;
padding: $btn-sm-side-margin;
&:hover {
@@ -357,10 +286,6 @@ $item-weight-max-width: 48px;
}
.sortable-link {
- display: block;
- text-overflow: ellipsis;
- white-space: nowrap;
- overflow: hidden;
line-height: 1.3;
}
}
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index d5c4712bd78..4926062f9ca 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -8,13 +8,6 @@
module AuthenticatesWithTwoFactor
extend ActiveSupport::Concern
- included do
- # This action comes from DeviseController, but because we call `sign_in`
- # manually, not skipping this action would cause a "You are already signed
- # in." error message to be shown upon successful login.
- skip_before_action :require_no_authentication, only: [:create], raise: false
- end
-
# Store the user's ID in the session for later retrieval and render the
# two factor code prompt
#
diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb
index c0c0160a827..cfff154c3dd 100644
--- a/app/controllers/concerns/milestone_actions.rb
+++ b/app/controllers/concerns/milestone_actions.rb
@@ -31,7 +31,7 @@ module MilestoneActions
format.html { redirect_to milestone_redirect_path }
format.json do
render json: tabs_json("shared/milestones/_labels_tab", {
- labels: @milestone.labels # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ labels: @milestone.labels.map { |label| label.present(issuable_subject: @milestone.parent) } # rubocop:disable Gitlab/ModuleWithInstanceVariables
})
end
end
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index e14abbf7c78..fc708400657 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -100,14 +100,14 @@ class Projects::BranchesController < Projects::ApplicationController
respond_to do |format|
format.html do
- flash_type = result[:status] == :error ? :alert : :notice
- flash[flash_type] = result[:message]
+ flash_type = result.error? ? :alert : :notice
+ flash[flash_type] = result.message
redirect_to project_branches_path(@project), status: :see_other
end
- format.js { head result[:return_code] }
- format.json { render json: { message: result[:message] }, status: result[:return_code] }
+ format.js { head result.http_status }
+ format.json { render json: { message: result.message }, status: result.http_status }
end
end
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index d1c5cef76fa..c4dff95a4b9 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -19,7 +19,7 @@ module Projects
redirect_to project_settings_ci_cd_path(@project)
else
- render 'show'
+ redirect_to project_settings_ci_cd_path(@project), alert: result[:message]
end
end
end
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 6943795e8ac..6fea61cf45d 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -8,6 +8,8 @@ class SessionsController < Devise::SessionsController
include Recaptcha::Verify
skip_before_action :check_two_factor_requirement, only: [:destroy]
+ # replaced with :require_no_authentication_without_flash
+ skip_before_action :require_no_authentication, only: [:new, :create]
prepend_before_action :check_initial_setup, only: [:new]
prepend_before_action :authenticate_with_two_factor,
@@ -15,6 +17,8 @@ class SessionsController < Devise::SessionsController
prepend_before_action :check_captcha, only: [:create]
prepend_before_action :store_redirect_uri, only: [:new]
prepend_before_action :ldap_servers, only: [:new, :create]
+ prepend_before_action :require_no_authentication_without_flash, only: [:new, :create]
+
before_action :auto_sign_in_with_provider, only: [:new]
before_action :load_recaptcha
@@ -54,6 +58,14 @@ class SessionsController < Devise::SessionsController
private
+ def require_no_authentication_without_flash
+ require_no_authentication
+
+ if flash[:alert] == I18n.t('devise.failure.already_authenticated')
+ flash[:alert] = nil
+ end
+ end
+
def captcha_enabled?
request.headers[CAPTCHA_HEADER] && Gitlab::Recaptcha.enabled?
end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index a07c3f90a91..76300e791e6 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -13,9 +13,7 @@ module LabelsHelper
# Link to a Label
#
- # label - Label object to link to
- # subject - Project/Group object which will be used as the context for the
- # label's link. If omitted, defaults to the label's own group/project.
+ # label - LabelPresenter object to link to
# type - The type of item the link will point to (:issue or
# :merge_request). If omitted, defaults to :issue.
# block - An optional block that will be passed to `link_to`, forming the
@@ -40,8 +38,8 @@ module LabelsHelper
# link_to_label(label) { "My Custom Label Text" }
#
# Returns a String
- def link_to_label(label, subject: nil, type: :issue, tooltip: true, css_class: nil, &block)
- link = label_filter_path(subject || label.subject, label, type: type)
+ def link_to_label(label, type: :issue, tooltip: true, css_class: nil, &block)
+ link = label.filter_path(type: type)
if block_given?
link_to link, class: css_class, &block
@@ -50,34 +48,6 @@ module LabelsHelper
end
end
- def label_filter_path(subject, label, type: :issue)
- case subject
- when Group
- send("#{type.to_s.pluralize}_group_path", # rubocop:disable GitlabSecurity/PublicSend
- subject,
- label_name: [label.name])
- when Project
- send("namespace_project_#{type.to_s.pluralize}_path", # rubocop:disable GitlabSecurity/PublicSend
- subject.namespace,
- subject,
- label_name: [label.name])
- end
- end
-
- def edit_label_path(label)
- case label
- when GroupLabel then edit_group_label_path(label.group, label)
- when ProjectLabel then edit_project_label_path(label.project, label)
- end
- end
-
- def destroy_label_path(label)
- case label
- when GroupLabel then group_label_path(label.group, label)
- when ProjectLabel then project_label_path(label.project, label)
- end
- end
-
def render_label(label, tooltip: true, link: nil, css: nil)
# if scoped label is used then EE wraps label tag with scoped label
# doc link
@@ -168,10 +138,6 @@ module LabelsHelper
end
end
- def can_subscribe_to_label_in_different_levels?(label)
- defined?(@project) && label.is_a?(GroupLabel)
- end
-
def label_subscription_status(label, project)
return 'group-level' if label.subscribed?(current_user)
return 'project-level' if label.subscribed?(current_user, project)
@@ -241,8 +207,8 @@ module LabelsHelper
"#{action} at #{level} level"
end
- def labels_sorted_by_title(labels)
- labels.sort_by(&:title)
+ def presented_labels_sorted_by_title(labels, subject)
+ labels.sort_by(&:title).map { |label| label.present(issuable_subject: subject) }
end
def label_dropdown_data(project, opts = {})
@@ -276,6 +242,10 @@ module LabelsHelper
klass.new(hash.slice(:color, :description, :title, :group_id, :project_id))
end
+ def issuable_types
+ ['issues', 'merge requests']
+ end
+
# Required for Banzai::Filter::LabelReferenceFilter
module_function :render_colored_label, :text_color_for_bg, :escape_once, :label_tooltip_title
end
diff --git a/app/helpers/markup_helper.rb b/app/helpers/markup_helper.rb
index be4fc2531ae..ad77f99fe44 100644
--- a/app/helpers/markup_helper.rb
+++ b/app/helpers/markup_helper.rb
@@ -83,7 +83,8 @@ module MarkupHelper
text = sanitize(
text,
tags: tags,
- attributes: Rails::Html::WhiteListSanitizer.allowed_attributes + ['style', 'data-src', 'data-name', 'data-unicode-version']
+ attributes: Rails::Html::WhiteListSanitizer.allowed_attributes +
+ %w(style data-src data-name data-unicode-version data-iid data-project-path data-mr-title)
)
# since <img> tags are stripped, this can leave empty <a> tags hanging around
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index b8edaf82c3d..bbd21eb0e78 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -642,6 +642,7 @@ module Ci
variables.append(key: 'CI_COMMIT_MESSAGE', value: git_commit_message.to_s)
variables.append(key: 'CI_COMMIT_TITLE', value: git_commit_full_title.to_s)
variables.append(key: 'CI_COMMIT_DESCRIPTION', value: git_commit_description.to_s)
+ variables.append(key: 'CI_COMMIT_REF_PROTECTED', value: (!!protected_ref?).to_s)
if merge_request_event? && merge_request
variables.append(key: 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA', value: source_sha.to_s)
diff --git a/app/models/concerns/cache_markdown_field.rb b/app/models/concerns/cache_markdown_field.rb
index 28ea51d6769..f90cd1ea690 100644
--- a/app/models/concerns/cache_markdown_field.rb
+++ b/app/models/concerns/cache_markdown_field.rb
@@ -15,7 +15,7 @@ module CacheMarkdownField
# Increment this number every time the renderer changes its output
CACHE_COMMONMARK_VERSION_START = 10
- CACHE_COMMONMARK_VERSION = 15
+ CACHE_COMMONMARK_VERSION = 16
# changes to these attributes cause the cache to be invalidates
INVALIDATED_BY = %w[author project].freeze
diff --git a/app/models/global_label.rb b/app/models/global_label.rb
index 572cb12b26a..7c020dd3b3d 100644
--- a/app/models/global_label.rb
+++ b/app/models/global_label.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class GlobalLabel
+ include Presentable
+
attr_accessor :title, :labels
alias_attribute :name, :title
@@ -23,4 +25,8 @@ class GlobalLabel
@labels = labels
@first_label = labels.find { |lbl| lbl.description.present? } || labels.first
end
+
+ def present(attributes)
+ super(attributes.merge(presenter_class: ::LabelPresenter))
+ end
end
diff --git a/app/models/global_milestone.rb b/app/models/global_milestone.rb
index fd17745b035..59f5a7703e2 100644
--- a/app/models/global_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -8,7 +8,9 @@ class GlobalMilestone
attr_reader :milestone
alias_attribute :name, :title
- delegate :title, :state, :due_date, :start_date, :participants, :project, :group, :expires_at, :closed?, :iid, :group_milestone?, :safe_title, :milestoneish_id, to: :milestone
+ delegate :title, :state, :due_date, :start_date, :participants, :project,
+ :group, :expires_at, :closed?, :iid, :group_milestone?, :safe_title,
+ :milestoneish_id, :parent, to: :milestone
def to_hash
{
diff --git a/app/models/internal_id.rb b/app/models/internal_id.rb
index 401b94d36e5..237401899db 100644
--- a/app/models/internal_id.rb
+++ b/app/models/internal_id.rb
@@ -87,12 +87,16 @@ class InternalId < ApplicationRecord
end
def available?
- @available_flag ||= ActiveRecord::Migrator.current_version >= REQUIRED_SCHEMA_VERSION # rubocop:disable Gitlab/PredicateMemoization
+ return true unless Rails.env.test?
+
+ Gitlab::SafeRequestStore.fetch(:internal_ids_available_flag) do
+ ActiveRecord::Migrator.current_version >= REQUIRED_SCHEMA_VERSION
+ end
end
# Flushes cached information about schema
def reset_column_information
- @available_flag = nil
+ Gitlab::SafeRequestStore[:internal_ids_available_flag] = nil
super
end
end
diff --git a/app/models/label.rb b/app/models/label.rb
index c7fff0d393e..e9085e8bd25 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -8,6 +8,7 @@ class Label < ApplicationRecord
include OptionallySearch
include Sortable
include FromUnion
+ include Presentable
cache_markdown_field :description, pipeline: :single_line
@@ -233,6 +234,10 @@ class Label < ApplicationRecord
attributes
end
+ def present(attributes)
+ super(attributes.merge(presenter_class: ::LabelPresenter))
+ end
+
private
def issues_count(user, params = {})
diff --git a/app/presenters/label_presenter.rb b/app/presenters/label_presenter.rb
new file mode 100644
index 00000000000..5227ef353c3
--- /dev/null
+++ b/app/presenters/label_presenter.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+class LabelPresenter < Gitlab::View::Presenter::Delegated
+ presents :label
+
+ def edit_path
+ case label
+ when GroupLabel then edit_group_label_path(label.group, label)
+ when ProjectLabel then edit_project_label_path(label.project, label)
+ end
+ end
+
+ def destroy_path
+ case label
+ when GroupLabel then group_label_path(label.group, label)
+ when ProjectLabel then project_label_path(label.project, label)
+ end
+ end
+
+ def filter_path(type: :issue)
+ case context_subject
+ when Group
+ send("#{type.to_s.pluralize}_group_path", # rubocop:disable GitlabSecurity/PublicSend
+ context_subject,
+ label_name: [label.name])
+ when Project
+ send("namespace_project_#{type.to_s.pluralize}_path", # rubocop:disable GitlabSecurity/PublicSend
+ context_subject.namespace,
+ context_subject,
+ label_name: [label.name])
+ end
+ end
+
+ def can_subscribe_to_label_in_different_levels?
+ issuable_subject.is_a?(Project) && label.is_a?(GroupLabel)
+ end
+
+ private
+
+ def context_subject
+ issuable_subject || label.try(:subject)
+ end
+end
diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb
index 4c3ac19f754..fd41ce54486 100644
--- a/app/services/delete_branch_service.rb
+++ b/app/services/delete_branch_service.rb
@@ -6,27 +6,25 @@ class DeleteBranchService < BaseService
branch = repository.find_branch(branch_name)
unless current_user.can?(:push_code, project)
- return error('You dont have push access to repo', 405)
+ return ServiceResponse.error(
+ message: 'You dont have push access to repo',
+ http_status: 405)
end
unless branch
- return error('No such branch', 404)
+ return ServiceResponse.error(
+ message: 'No such branch',
+ http_status: 404)
end
if repository.rm_branch(current_user, branch_name)
- success('Branch was deleted')
+ ServiceResponse.success(message: 'Branch was deleted')
else
- error('Failed to remove branch')
+ ServiceResponse.error(
+ message: 'Failed to remove branch',
+ http_status: 400)
end
rescue Gitlab::Git::PreReceiveError => ex
- error(ex.message)
- end
-
- def error(message, return_code = 400)
- super(message).merge(return_code: return_code)
- end
-
- def success(message)
- super().merge(message: message)
+ ServiceResponse.error(message: ex.message, http_status: 400)
end
end
diff --git a/app/services/service_response.rb b/app/services/service_response.rb
new file mode 100644
index 00000000000..1de30e68d87
--- /dev/null
+++ b/app/services/service_response.rb
@@ -0,0 +1,31 @@
+# frozen_string_literal: true
+
+class ServiceResponse
+ def self.success(message: nil)
+ new(status: :success, message: message)
+ end
+
+ def self.error(message:, http_status: nil)
+ new(status: :error, message: message, http_status: http_status)
+ end
+
+ attr_reader :status, :message, :http_status
+
+ def initialize(status:, message: nil, http_status: nil)
+ self.status = status
+ self.message = message
+ self.http_status = http_status
+ end
+
+ def success?
+ status == :success
+ end
+
+ def error?
+ status == :error
+ end
+
+ private
+
+ attr_writer :status, :message, :http_status
+end
diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml
index dbb7224f5f9..6d934654c5d 100644
--- a/app/views/admin/labels/_label.html.haml
+++ b/app/views/admin/labels/_label.html.haml
@@ -1,5 +1,5 @@
%li.label-list-item{ id: dom_id(label) }
- = render "shared/label_row", label: label
+ = render "shared/label_row", label: label.present(issuable_subject: nil)
.label-actions-list
= link_to edit_admin_label_path(label), class: 'btn btn-transparent label-action has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do
= sprite_icon('pencil')
diff --git a/app/views/groups/labels/index.html.haml b/app/views/groups/labels/index.html.haml
index 5cf3193bc62..a8358704b03 100644
--- a/app/views/groups/labels/index.html.haml
+++ b/app/views/groups/labels/index.html.haml
@@ -1,7 +1,6 @@
- @no_container = true
-- page_title "Labels"
+- page_title 'Labels'
- can_admin_label = can?(current_user, :admin_label, @group)
-- issuables = ['issues', 'merge requests']
- search = params[:search]
- subscribed = params[:subscribed]
- labels_or_filters = @labels.exists? || search.present? || subscribed.present?
@@ -14,11 +13,11 @@
.labels-container.prepend-top-5
- if @labels.any?
.text-muted
- = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuables.to_sentence }
+ = _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuable_types.to_sentence }
.other-labels
%h5= _('Labels')
%ul.content-list.manage-labels-list.js-other-labels
- = render partial: 'shared/label', subject: @group, collection: @labels, as: :label, locals: { use_label_priority: false }
+ = render partial: 'shared/label', collection: @labels, as: :label, locals: { use_label_priority: false, subject: @group }
= paginate @labels, theme: 'gitlab'
- elsif search.present?
.nothing-here-block
diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml
index a88b04eccbb..c4670869c93 100644
--- a/app/views/import/gitea/new.html.haml
+++ b/app/views/import/gitea/new.html.haml
@@ -2,18 +2,18 @@
- header_title _("Projects"), root_path
%h3.page-title
- = custom_icon('go_logo')
+ = custom_icon('gitea_logo')
= _('Import Projects from Gitea')
%p
- - link_to_personal_token = link_to(_('Personal Access Token'), 'https://github.com/gogits/go-gogs-client/wiki#access-token')
+ - link_to_personal_token = link_to(_('Personal Access Token'), 'https://docs.gitea.io/en-us/api-usage/#authentication-via-the-api')
= _('To get started, please enter your Gitea Host URL and a %{link_to_personal_token}.').html_safe % { link_to_personal_token: link_to_personal_token }
= form_tag personal_access_token_import_gitea_path do
.form-group.row
= label_tag :gitea_host_url, _('Gitea Host URL'), class: 'col-form-label col-sm-2'
.col-sm-4
- = text_field_tag :gitea_host_url, nil, placeholder: 'https://try.gitea.io', class: 'form-control'
+ = text_field_tag :gitea_host_url, nil, placeholder: 'https://gitea.com', class: 'form-control'
.form-group.row
= label_tag :personal_access_token, _('Personal Access Token'), class: 'col-form-label col-sm-2'
.col-sm-4
diff --git a/app/views/import/gitea/status.html.haml b/app/views/import/gitea/status.html.haml
index 88244fde16b..ef0693e73c3 100644
--- a/app/views/import/gitea/status.html.haml
+++ b/app/views/import/gitea/status.html.haml
@@ -1,7 +1,7 @@
- page_title _("Gitea Import")
- header_title _("Projects"), root_path
%h3.page-title
- = custom_icon('go_logo')
+ = custom_icon('gitea_logo')
= _('Import Projects from Gitea')
= render 'import/githubish_status', provider: 'gitea'
diff --git a/app/views/projects/_import_project_pane.html.haml b/app/views/projects/_import_project_pane.html.haml
index 2b425f18389..9c854369c93 100644
--- a/app/views/projects/_import_project_pane.html.haml
+++ b/app/views/projects/_import_project_pane.html.haml
@@ -50,7 +50,7 @@
- if gitea_import_enabled?
%div
= link_to new_import_gitea_path, class: 'btn import_gitea', data: { track_label: "#{track_label}", track_event: "click_button", track_property: "gitea" } do
- = custom_icon('go_logo')
+ = custom_icon('gitea_logo')
Gitea
- if git_import_enabled?
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 377b2a6d8d9..945d1b00b08 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -36,8 +36,8 @@
= issue.due_date.to_s(:medium)
- if issue.labels.any?
&nbsp;
- - labels_sorted_by_title(issue.labels).each do |label|
- = link_to_label(label, subject: issue.project, css_class: 'label-link')
+ - presented_labels_sorted_by_title(issue.labels, issue.project).each do |label|
+ = link_to_label(label, css_class: 'label-link')
.issuable-meta
%ul.controls
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index 57d13b99b21..0bf664d5b66 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -77,6 +77,8 @@
= edited_time_ago_with_tooltip(@issue, placement: 'bottom', html_class: 'issue-edited-ago js-issue-edited-ago')
+ = render_if_exists 'projects/issues/related_issues'
+
#js-related-merge-requests{ data: { endpoint: expose_url(api_v4_projects_issues_related_merge_requests_path(id: @project.id, issue_iid: @issue.iid)), project_namespace: @project.namespace.path, project_path: @project.path } }
- if can?(current_user, :download_code, @project)
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index 5d73d832170..511d7a82d1b 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -25,7 +25,7 @@
#js-priority-labels-empty-state.priority-labels-empty-state{ class: "#{'hidden' unless @prioritized_labels.empty? && search.blank?}" }
= render 'shared/empty_states/priority_labels'
- if @prioritized_labels.present?
- = render partial: 'shared/label', subject: @project, collection: @prioritized_labels, as: :label, locals: { force_priority: true }
+ = render partial: 'shared/label', collection: @prioritized_labels, as: :label, locals: { force_priority: true, subject: @project }
- elsif search.present?
.nothing-here-block
= _('No prioritised labels with such name or description')
@@ -34,7 +34,7 @@
.other-labels
%h5{ class: ('hide' if hide) }= _('Other Labels')
.content-list.manage-labels-list.js-other-labels
- = render partial: 'shared/label', subject: @project, collection: @labels, as: :label
+ = render partial: 'shared/label', collection: @labels, as: :label, locals: { subject: @project }
= paginate @labels, theme: 'gitlab'
- elsif search.present?
.other-labels
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 47c8e3d73f5..67e5e4ca62d 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -34,8 +34,8 @@
= merge_request.target_branch
- if merge_request.labels.any?
&nbsp;
- - labels_sorted_by_title(merge_request.labels).each do |label|
- = link_to_label(label, subject: merge_request.project, type: :merge_request, css_class: 'label-link')
+ - presented_labels_sorted_by_title(merge_request.labels, merge_request.project).each do |label|
+ = link_to_label(label, type: :merge_request, css_class: 'label-link')
.issuable-meta
%ul.controls
diff --git a/app/views/shared/_delete_label_modal.html.haml b/app/views/shared/_delete_label_modal.html.haml
index dbd3bbb43af..6bd8cadd7d9 100644
--- a/app/views/shared/_delete_label_modal.html.haml
+++ b/app/views/shared/_delete_label_modal.html.haml
@@ -9,13 +9,13 @@
.modal-body
%p
%strong= label.name
- %span will be permanently deleted from #{label.is_a?(ProjectLabel)? label.project.name : label.group.name}. This cannot be undone.
+ %span will be permanently deleted from #{label.subject.name}. This cannot be undone.
.modal-footer
%a{ href: '#', data: { dismiss: 'modal' }, class: 'btn btn-default' } Cancel
= link_to 'Delete label',
- destroy_label_path(label),
+ label.destroy_path,
title: 'Delete',
method: :delete,
class: 'btn btn-remove'
diff --git a/app/views/shared/_label.html.haml b/app/views/shared/_label.html.haml
index 21ea188d7b3..2b4a24a001f 100644
--- a/app/views/shared/_label.html.haml
+++ b/app/views/shared/_label.html.haml
@@ -1,13 +1,13 @@
+- label = label.present(issuable_subject: local_assigns[:subject])
- label_css_id = dom_id(label)
- status = label_subscription_status(label, @project).inquiry if current_user
-- subject = local_assigns[:subject]
- use_label_priority = local_assigns.fetch(:use_label_priority, false)
- force_priority = local_assigns.fetch(:force_priority, use_label_priority ? label.priority.present? : false)
- toggle_subscription_path = toggle_subscription_label_path(label, @project) if current_user
- tooltip_title = label_status_tooltip(label, status) if status
%li.label-list-item{ id: label_css_id, data: { id: label.id } }
- = render "shared/label_row", label: label, subject: subject, force_priority: force_priority
+ = render "shared/label_row", label: label, force_priority: force_priority
%ul.label-actions-list
- if @project
%li.inline
@@ -21,7 +21,7 @@
= sprite_icon('star')
- if can?(current_user, :admin_label, label)
%li.inline
- = link_to edit_label_path(label), class: 'btn btn-transparent label-action edit has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do
+ = link_to label.edit_path, class: 'btn btn-transparent label-action edit has-tooltip', title: _('Edit'), data: { placement: 'bottom' }, aria_label: _('Edit') do
= sprite_icon('pencil')
- if can?(current_user, :admin_label, label)
%li.inline
@@ -48,7 +48,7 @@
%button.text-danger.remove-row{ type: 'button' }= _('Delete')
- if current_user
%li.inline.label-subscription
- - if can_subscribe_to_label_in_different_levels?(label)
+ - if label.can_subscribe_to_label_in_different_levels?
%button.js-unsubscribe-button.label-subscribe-button.btn.btn-default{ class: ('hidden' if status.unsubscribed?), data: { url: toggle_subscription_path, toggle: 'tooltip' }, title: tooltip_title }
%span= _('Unsubscribe')
.dropdown.dropdown-group-label{ class: ('hidden' unless status.unsubscribed?) }
diff --git a/app/views/shared/_label_row.html.haml b/app/views/shared/_label_row.html.haml
index 9d1648fbf70..a1aab2e6a08 100644
--- a/app/views/shared/_label_row.html.haml
+++ b/app/views/shared/_label_row.html.haml
@@ -1,4 +1,3 @@
-- subject = local_assigns[:subject]
- force_priority = local_assigns.fetch(:force_priority, false)
- subject_or_group_defined = defined?(@project) || defined?(@group)
- show_label_issues_link = subject_or_group_defined && show_label_issuables_link?(label, :issues, project: @project)
@@ -14,11 +13,11 @@
%ul.label-links
- if show_label_issues_link
%li.label-link-item.inline
- = link_to_label(label, subject: subject) { 'Issues' }
+ = link_to_label(label) { 'Issues' }
- if show_label_merge_requests_link
&middot;
%li.label-link-item.inline
- = link_to_label(label, subject: subject, type: :merge_request) { _('Merge requests') }
+ = link_to_label(label, type: :merge_request) { _('Merge requests') }
- if force_priority
&middot;
%li.label-link-item.priority-badge.js-priority-badge.inline.prepend-left-10
diff --git a/app/views/shared/_sidebar_toggle_button.html.haml b/app/views/shared/_sidebar_toggle_button.html.haml
index 2530db986e0..aa428f9fe73 100644
--- a/app/views/shared/_sidebar_toggle_button.html.haml
+++ b/app/views/shared/_sidebar_toggle_button.html.haml
@@ -1,8 +1,8 @@
%a.toggle-sidebar-button.js-toggle-sidebar{ role: "button", type: "button", title: "Toggle sidebar" }
= sprite_icon('angle-double-left', css_class: 'icon-angle-double-left')
= sprite_icon('angle-double-right', css_class: 'icon-angle-double-right')
- %span.collapse-text Collapse sidebar
+ %span.collapse-text _("Collapse sidebar")
= button_tag class: 'close-nav-button', type: 'button' do
= sprite_icon('close', size: 16)
- %span.collapse-text Close sidebar
+ %span.collapse-text _("Close sidebar")
diff --git a/app/views/shared/icons/_gitea_logo.svg.erb b/app/views/shared/icons/_gitea_logo.svg.erb
new file mode 100644
index 00000000000..c8ddbc5535e
--- /dev/null
+++ b/app/views/shared/icons/_gitea_logo.svg.erb
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?><!-- Created with Inkscape (http://www.inkscape.org/) --><svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="<%= size %>" height="<%= size %>" viewBox="0 0 135.46667 135.46667" version="1.1" id="svg8" sodipodi:docname="logo.svg" inkscape:version="0.92.1 r15371" inkscape:export-filename="" inkscape:export-xdpi="48.000004" inkscape:export-ydpi="48.000004" style="zoom: 1;"><defs id="defs2"></defs><sodipodi:namedview id="base" pagecolor="#ffffff" bordercolor="#666666" borderopacity="1.0" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:zoom="0.70710678" inkscape:cx="418.13805" inkscape:cy="177.57445" inkscape:document-units="mm" inkscape:current-layer="layer2" showgrid="false" units="px" width="256px" showguides="false" inkscape:window-width="1920" inkscape:window-height="1137" inkscape:window-x="1912" inkscape:window-y="-8" inkscape:window-maximized="1" inkscape:pagecheckerboard="false" inkscape:measure-start="283.373,243.952" inkscape:measure-end="290.267,236.527"><sodipodi:guide position="0,0" orientation="0,512" id="guide3699" inkscape:locked="false"></sodipodi:guide><sodipodi:guide position="135.46667,0" orientation="-512,0" id="guide3701" inkscape:locked="false"></sodipodi:guide><sodipodi:guide position="135.46667,135.46667" orientation="0,-512" id="guide3703" inkscape:locked="false"></sodipodi:guide><sodipodi:guide position="0,135.46667" orientation="512,0" id="guide3705" inkscape:locked="false"></sodipodi:guide></sodipodi:namedview><metadata id="metadata5"><rdf:RDF><cc:Work rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"></dc:type><dc:title></dc:title></cc:Work></rdf:RDF></metadata><g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,-161.53334)" style="display:inline"><path d="M27.709937,195.15095 c-9.546573,-0.0272 -22.3392732,6.79805 -21.6317552,23.90397 c1.105534,26.72889 25.4565952,29.20839 35.1916502,29.42301 c1.068023,5.01357 12.521798,22.30563 21.001818,23.21667 h37.15277 c22.27763,-1.66785 38.9607,-75.75671 26.59321,-76.03825 c-46.781583,2.47691 -49.995146,2.13838 -88.599758,0 c-2.495053,-0.0266 -5.972321,-0.49474 -9.707935,-0.5054 z m2.491319,9.45886 c1.351378,13.69267 3.555849,21.70359 8.018216,33.94345 c-11.382872,-1.50473 -21.069822,-5.22443 -22.851515,-19.10984 c-0.950962,-7.4112 2.390428,-15.16769 14.833299,-14.83361 z " id="path3722" sodipodi:nodetypes="sscccccsccsc" inkscape:connector-curvature="0" style="fill:#000;fill-opacity:1;stroke:#000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"></path></g><g inkscape:groupmode="layer" id="layer2" inkscape:label="Layer 2" style="display:inline"><rect style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.24757317;stroke-opacity:1" id="rect4599" width="34.762054" height="34.762054" x="87.508659" y="18.291576" transform="rotate(25.914715)" ry="5.4825778"></rect><path style="display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.26644793px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" d="m 79.804947,57.359056 3.241146,1.609954 V 35.255731 h -3.262698 z" id="path4525" inkscape:connector-curvature="0" sodipodi:nodetypes="ccccc"></path></g><g inkscape:groupmode="layer" id="layer3" inkscape:label="Layer 3" style="display:inline"><g style="display:inline" id="g4539"><circle style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" id="path4606" cy="90.077766" r="3.4745038" cx="49.064713" transform="rotate(-19.796137)"></circle><circle style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" id="path4606-3" cy="102.1049" r="3.4745038" cx="36.810425" transform="rotate(-19.796137)"></circle><circle style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-opacity:1" id="path4606-1" cy="111.43928" r="3.4745038" cx="46.484283" transform="rotate(-19.796137)"></circle><rect height="27.261492" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:0.27444693;stroke-opacity:1" x="97.333458" y="18.061695" id="rect4629-8" width="2.6726954" transform="rotate(26.024158)"></rect><path d="M76.558096,68.116343 c12.97589,6.395378 13.012989,4.101862 4.890858,20.907244 " id="path4514" sodipodi:nodetypes="cc" inkscape:connector-curvature="0" style="fill:none;stroke:#000;stroke-width:2.68000007;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"></path></g></g></svg> \ No newline at end of file
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index d4be7289a49..2c185549b24 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -106,7 +106,7 @@
.value.issuable-show-labels.dont-hide.hide-collapsed.qa-labels-block{ class: ("has-labels" if selected_labels.any?) }
- if selected_labels.any?
- selected_labels.each do |label_hash|
- = render_label(label_from_hash(label_hash), link: sidebar_label_filter_path(issuable_sidebar[:project_issuables_path], label_hash[:title]))
+ = render_label(label_from_hash(label_hash).present(issuable_subject: nil), link: sidebar_label_filter_path(issuable_sidebar[:project_issuables_path], label_hash[:title]))
- else
%span.no-value
= _('None')
diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml
index 5863f52aa78..ae3ab2adfd0 100644
--- a/app/views/shared/milestones/_issuable.html.haml
+++ b/app/views/shared/milestones/_issuable.html.haml
@@ -21,7 +21,7 @@
%span.issuable-number= issuable.to_reference
- labels.each do |label|
- = render_label(label, link: polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }))
+ = render_label(label.present(issuable_subject: project), link: polymorphic_path(issuable_type_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }))
%span.assignee-icon
- assignees.each do |assignee|
diff --git a/changelogs/unreleased/27424-tklk-gitea-logo.yml b/changelogs/unreleased/27424-tklk-gitea-logo.yml
new file mode 100644
index 00000000000..0d41bb39aad
--- /dev/null
+++ b/changelogs/unreleased/27424-tklk-gitea-logo.yml
@@ -0,0 +1,5 @@
+---
+title: "Use official Gitea logo in importer"
+merge_request: 27424
+author: Matti Ranta (@techknowlogick)
+type: added \ No newline at end of file
diff --git a/changelogs/unreleased/54656-500-error-on-save-of-general-pipeline-settings-timeout.yml b/changelogs/unreleased/54656-500-error-on-save-of-general-pipeline-settings-timeout.yml
new file mode 100644
index 00000000000..8b4f4894048
--- /dev/null
+++ b/changelogs/unreleased/54656-500-error-on-save-of-general-pipeline-settings-timeout.yml
@@ -0,0 +1,5 @@
+---
+title: Fix 500 in general pipeline settings when passing an invalid build timeout.
+merge_request: 27416
+author:
+type: fixed
diff --git a/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml b/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml
deleted file mode 100644
index df6e6ea4be3..00000000000
--- a/changelogs/unreleased/60500-disable-jit-kubernetes-resource-creation-for-project-level-clusters.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Disable just-in-time Kubernetes resource creation for project level clusters
-merge_request: 27352
-author:
-type: changed
diff --git a/changelogs/unreleased/60540-merge-request-popover-is-not-working-on-the-to-do-page.yml b/changelogs/unreleased/60540-merge-request-popover-is-not-working-on-the-to-do-page.yml
new file mode 100644
index 00000000000..7b5fae016bb
--- /dev/null
+++ b/changelogs/unreleased/60540-merge-request-popover-is-not-working-on-the-to-do-page.yml
@@ -0,0 +1,5 @@
+---
+title: Fix MR popover on ToDos page
+merge_request: 27382
+author:
+type: fixed
diff --git a/changelogs/unreleased/60569-timeline-entry-label-link-is-not-applying-the-filter-on-issues.yml b/changelogs/unreleased/60569-timeline-entry-label-link-is-not-applying-the-filter-on-issues.yml
deleted file mode 100644
index 5319373ec4b..00000000000
--- a/changelogs/unreleased/60569-timeline-entry-label-link-is-not-applying-the-filter-on-issues.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Fix filtering of labels from system note link
-merge_request: 27507
-author:
-type: fixed
diff --git a/changelogs/unreleased/add-ci-variable-protected-ref.yml b/changelogs/unreleased/add-ci-variable-protected-ref.yml
new file mode 100644
index 00000000000..150ddcc21ad
--- /dev/null
+++ b/changelogs/unreleased/add-ci-variable-protected-ref.yml
@@ -0,0 +1,5 @@
+---
+title: Add CI_COMMIT_REF_PROTECTED CI variable
+merge_request: 26716
+author: Jason van den Hurk
+type: added
diff --git a/changelogs/unreleased/ce-remove-already-signed-in.yml b/changelogs/unreleased/ce-remove-already-signed-in.yml
new file mode 100644
index 00000000000..70bed136ced
--- /dev/null
+++ b/changelogs/unreleased/ce-remove-already-signed-in.yml
@@ -0,0 +1,5 @@
+---
+title: Remove "You are already signed in" banner
+merge_request: 27377
+author:
+type: other
diff --git a/changelogs/unreleased/fj-60827-fix-web-strategy-error.yml b/changelogs/unreleased/fj-60827-fix-web-strategy-error.yml
new file mode 100644
index 00000000000..ad707ca0225
--- /dev/null
+++ b/changelogs/unreleased/fj-60827-fix-web-strategy-error.yml
@@ -0,0 +1,5 @@
+---
+title: Fix bug when project export to remote url fails
+merge_request: 27614
+author:
+type: fixed
diff --git a/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml b/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml
deleted file mode 100644
index c1669a484aa..00000000000
--- a/changelogs/unreleased/jc-upgrade-gitaly-1-34-0.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Upgrade Gitaly to 1.34.0
-merge_request: 27494
-author:
-type: fixed
diff --git a/changelogs/unreleased/sh-bump-ruby-required-version-check.yml b/changelogs/unreleased/sh-bump-ruby-required-version-check.yml
deleted file mode 100644
index b5b6eb87650..00000000000
--- a/changelogs/unreleased/sh-bump-ruby-required-version-check.yml
+++ /dev/null
@@ -1,5 +0,0 @@
----
-title: Bump required Ruby version check to 2.5.3
-merge_request: 27495
-author:
-type: other
diff --git a/changelogs/unreleased/sh-disable-internal-ids-available-check.yml b/changelogs/unreleased/sh-disable-internal-ids-available-check.yml
new file mode 100644
index 00000000000..069a9ba7d69
--- /dev/null
+++ b/changelogs/unreleased/sh-disable-internal-ids-available-check.yml
@@ -0,0 +1,5 @@
+---
+title: Always use internal ID tables in development and production
+merge_request: 27544
+author:
+type: fixed
diff --git a/changelogs/unreleased/sh-upgrade-grpc-and-protobuf.yml b/changelogs/unreleased/sh-upgrade-grpc-and-protobuf.yml
new file mode 100644
index 00000000000..a43a59a4f8a
--- /dev/null
+++ b/changelogs/unreleased/sh-upgrade-grpc-and-protobuf.yml
@@ -0,0 +1,5 @@
+---
+title: Bump gRPC to 1.19.0 and protobuf to 3.7.1
+merge_request: 27086
+author:
+type: other
diff --git a/changelogs/unreleased/update-workhorse-master.yml b/changelogs/unreleased/update-workhorse-master.yml
new file mode 100644
index 00000000000..97e2e891ab1
--- /dev/null
+++ b/changelogs/unreleased/update-workhorse-master.yml
@@ -0,0 +1,5 @@
+---
+title: Update Workhorse to v8.7.0
+merge_request: 27630
+author:
+type: fixed
diff --git a/danger/ce_ee_vue_templates/Dangerfile b/danger/ce_ee_vue_templates/Dangerfile
new file mode 100644
index 00000000000..f7715eb2a89
--- /dev/null
+++ b/danger/ce_ee_vue_templates/Dangerfile
@@ -0,0 +1,56 @@
+# frozen_string_literal: true
+require 'cgi'
+
+def get_vue_files_with_ce_and_ee_versions(files)
+ files.select do |file|
+ if file.end_with?('.vue')
+ counterpart_path = if file.start_with?('ee/')
+ file.delete_prefix('ee/')
+ else
+ "ee/#{file}"
+ end
+
+ escaped_path = CGI.escape(counterpart_path)
+ api_endpoint = "https://gitlab.com/api/v4/projects/gitlab-org%2Fgitlab-ee/repository/files/#{escaped_path}?ref=master"
+ response = HTTParty.get(api_endpoint) # rubocop:disable Gitlab/HTTParty
+ response.code != 404
+ else
+ false
+ end
+ end
+end
+
+vue_candidates = get_vue_files_with_ce_and_ee_versions(helper.all_changed_files)
+
+return if vue_candidates.empty?
+
+message 'This merge request includes changes to Vue files that have both CE and EE versions.'
+
+markdown(<<~MARKDOWN)
+ ## Vue `<template>` in CE and EE
+
+ Some Vue files in CE have a counterpart in EE.
+ (For example, `path/to/file.vue` and `ee/path/to/file.vue`.)
+
+ When run in the context of CE, the `<template>` of the CE Vue file is used.
+ When run in the context of EE, the `<template>` of the EE Vue file is used.
+
+ It's easy to accidentally make a change to a CE `<template>` that _should_
+ appear in both CE and EE without making the change in both places.
+ When this happens, the change only takes effect in CE.
+
+ The following Vue files were changed as part of this merge request that
+ include both a CE and EE version of the file:
+
+ * #{vue_candidates.map { |path| "`#{path}`" }.join("\n* ")}
+
+ If you made a change to the `<template>` of any of these Vue files that
+ should be visible in both CE and EE, please ensure you have made your
+ change to both versions of the file.
+
+ ### A better alternative
+
+ An even _better_ alternative is to refactor this component to only use
+ a single template for both CE and EE. More info on this approach here:
+ https://docs.gitlab.com/ee/development/ee_features.html#template-tag
+MARKDOWN
diff --git a/db/fixtures/development/09_issues.rb b/db/fixtures/development/09_issues.rb
index 926401d8b9e..582a5203d1d 100644
--- a/db/fixtures/development/09_issues.rb
+++ b/db/fixtures/development/09_issues.rb
@@ -1,23 +1,5 @@
require './spec/support/sidekiq'
Gitlab::Seeder.quiet do
- Project.all.each do |project|
- 10.times do
- label_ids = project.labels.pluck(:id).sample(3)
- label_ids += project.group.labels.sample(3) if project.group
-
- issue_params = {
- title: FFaker::Lorem.sentence(6),
- description: FFaker::Lorem.sentence,
- state: ['opened', 'closed'].sample,
- milestone: project.milestones.sample,
- assignees: [project.team.users.sample],
- created_at: rand(12).months.ago,
- label_ids: label_ids
- }
-
- Issues::CreateService.new(project, project.team.users.sample, issue_params).execute
- print '.'
- end
- end
+ Rake::Task["gitlab:seed:issues"].invoke
end
diff --git a/doc/ci/variables/predefined_variables.md b/doc/ci/variables/predefined_variables.md
index 4c562138b98..40458137752 100644
--- a/doc/ci/variables/predefined_variables.md
+++ b/doc/ci/variables/predefined_variables.md
@@ -84,6 +84,7 @@ future GitLab releases.**
| `CI_PROJECT_PATH_SLUG` | 9.3 | all | `$CI_PROJECT_PATH` lowercased and with everything except `0-9` and `a-z` replaced with `-`. Use in URLs and domain names. |
| `CI_PROJECT_URL` | 8.10 | 0.5 | The HTTP(S) address to access project |
| `CI_PROJECT_VISIBILITY` | 10.3 | all | The project visibility (internal, private, public) |
+| `CI_COMMIT_REF_PROTECTED` | 11.11 | all | If the job is running on a protected branch |
| `CI_REGISTRY` | 8.10 | 0.5 | If the Container Registry is enabled it returns the address of GitLab's Container Registry |
| `CI_REGISTRY_IMAGE` | 8.10 | 0.5 | If the Container Registry is enabled for the project it returns the address of the registry tied to the specific project |
| `CI_REGISTRY_PASSWORD` | 9.0 | all | The password to use to push containers to the GitLab Container Registry |
diff --git a/doc/development/contributing/issue_workflow.md b/doc/development/contributing/issue_workflow.md
index 827b3d7681a..0e1ab8663ed 100644
--- a/doc/development/contributing/issue_workflow.md
+++ b/doc/development/contributing/issue_workflow.md
@@ -340,13 +340,14 @@ addressed.
In order to track things that can be improved in GitLab's codebase,
we use the ~"technical debt" label in [GitLab's issue tracker][ce-tracker].
-For user experience improvements, we use the ~"UX debt" label.
+For missed user experience requirements, we use the ~"UX debt" label.
These labels should be added to issues that describe things that can be improved,
shortcuts that have been taken, features that need additional attention, and all
other things that have been left behind due to high velocity of development.
For example, code that needs refactoring should use the ~"technical debt" label,
-user experience refinements should use the ~"UX debt" label.
+something that didn't ship according to our Design System guidelines should
+use the ~"UX debt" label.
Everyone can create an issue, though you may need to ask for adding a specific
label, if you do not have permissions to do it by yourself. Additional labels
diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md
index dcb32c89f65..1ae69127295 100644
--- a/doc/development/rake_tasks.md
+++ b/doc/development/rake_tasks.md
@@ -12,6 +12,22 @@ The `setup` task is an alias for `gitlab:setup`.
This tasks calls `db:reset` to create the database, calls `add_limits_mysql` that adds limits to the database schema in case of a MySQL database and finally it calls `db:seed_fu` to seed the database.
Note: `db:setup` calls `db:seed` but this does nothing.
+### Seeding issues for all or a given project
+
+You can seed issues for all or a given project with the `gitlab:seed:issues`
+task:
+
+```shell
+# All projects
+bin/rake gitlab:seed:issues
+
+# A specific project
+bin/rake "gitlab:seed:issues[group-path/project-path]"
+```
+
+By default, this seeds an average of 2 issues per week for the last 5 weeks per
+project.
+
### Automation
If you're very sure that you want to **wipe the current database** and refill
diff --git a/doc/user/project/integrations/gitlab_slack_application.md b/doc/user/project/integrations/gitlab_slack_application.md
new file mode 100644
index 00000000000..8e062ca627b
--- /dev/null
+++ b/doc/user/project/integrations/gitlab_slack_application.md
@@ -0,0 +1,65 @@
+# GitLab Slack application **[FREE ONLY]**
+
+NOTE: **Note:**
+The GitLab Slack application is only configurable for GitLab.com. It will **not**
+work for on-premises installations where you can configure the
+[Slack slash commands](slack_slash_commands.md) service instead. We're working
+with Slack on making this configurable for all GitLab installations, but there's
+no ETA.
+It was first introduced in GitLab 9.4 and distributed to Slack App Directory in
+GitLab 10.2.
+
+Slack provides a native application which you can enable via your project's
+integrations on GitLab.com.
+
+## Slack App Directory
+
+The simplest way to enable the GitLab Slack application for your workspace is to
+install the [GitLab application](https://slack-platform.slack.com/apps/A676ADMV5-gitlab) from
+the [Slack App Directory](https://slack.com/apps).
+
+Clicking install will take you to the
+[GitLab Slack application landing page](https://gitlab.com/profile/slack/edit)
+where you can select a project to enable the GitLab Slack application for.
+
+![GitLab Slack application landing page](img/gitlab_slack_app_landing_page.png)
+
+## Configuration
+
+Alternatively, you can configure the Slack application with a project's
+integration settings.
+
+Keep in mind that you need to have the appropriate permissions for your Slack
+team in order to be able to install a new application, read more in Slack's
+docs on [Adding an app to your team][slack-docs].
+
+To enable GitLab's service for your Slack team:
+
+1. Go to your project's **Settings > Integration > Slack application** (only
+ visible on GitLab.com)
+1. Click the "Add to Slack" button
+
+That's all! You can now start using the Slack slash commands.
+
+## Usage
+
+After confirming the installation, you, and everyone else in your Slack team,
+can use all the [slash commands].
+
+When you perform your first slash command you will be asked to authorize your
+Slack user on GitLab.com.
+
+The only difference with the [manually configurable Slack slash commands][slack-manual]
+is that all the commands should be prefixed with the `/gitlab` keyword.
+We are working on making this configurable in the future.
+
+For example, to show the issue number `1001` under the `gitlab-org/gitlab-ce`
+project, you would do:
+
+```
+/gitlab gitlab-org/gitlab-ce issue show 1001
+```
+
+[slack-docs]: https://get.slack.help/hc/en-us/articles/202035138-Adding-apps-to-your-team
+[slash commands]: ../../../integration/slash_commands.md
+[slack-manual]: slack_slash_commands.md
diff --git a/doc/user/project/integrations/img/gitlab_slack_app_landing_page.png b/doc/user/project/integrations/img/gitlab_slack_app_landing_page.png
new file mode 100644
index 00000000000..57cd35c9f5d
--- /dev/null
+++ b/doc/user/project/integrations/img/gitlab_slack_app_landing_page.png
Binary files differ
diff --git a/doc/user/project/integrations/project_services.md b/doc/user/project/integrations/project_services.md
index 42c7824a125..339e6873c41 100644
--- a/doc/user/project/integrations/project_services.md
+++ b/doc/user/project/integrations/project_services.md
@@ -14,8 +14,7 @@ want to configure.
![Project services list](img/project_services.png)
-Below, you will find a list of the currently supported ones accompanied with
-comprehensive documentation.
+Below, you will find a list of the currently supported ones accompanied with comprehensive documentation.
## Services
@@ -46,7 +45,8 @@ Click on the service links to see further configuration instructions and details
| Packagist | Update your project on Packagist, the main Composer repository |
| Pipelines emails | Email the pipeline status to a list of recipients |
| [Slack Notifications](slack.md) | Send GitLab events (e.g. issue created) to Slack as notifications |
-| [Slack slash commands](slack_slash_commands.md) | Use slash commands in Slack to control GitLab |
+| [Slack slash commands](slack_slash_commands.md) **[CORE ONLY]** | Use slash commands in Slack to control GitLab |
+| [GitLab Slack application](gitlab_slack_application.md) **[FREE ONLY]** | Use Slack's official application |
| PivotalTracker | Project Management Software (Source Commits Endpoint) |
| [Prometheus](prometheus.md) | Monitor the performance of your deployed apps |
| Pushover | Pushover makes it easy to get real-time notifications on your Android device, iPhone, iPad, and Desktop |
diff --git a/doc/user/project/integrations/slack_slash_commands.md b/doc/user/project/integrations/slack_slash_commands.md
index c267da69bb3..371e78ca3a4 100644
--- a/doc/user/project/integrations/slack_slash_commands.md
+++ b/doc/user/project/integrations/slack_slash_commands.md
@@ -1,10 +1,15 @@
-# Slack slash commands
+# Slack slash commands **[CORE ONLY]**
-> Introduced in GitLab 8.15
+> Introduced in GitLab 8.15.
-Slack slash commands allow you to control GitLab and view content right inside Slack, without having to leave it. This requires configurations in both Slack and GitLab.
+Slack slash commands allow you to control GitLab and view content right inside
+Slack, without having to leave it. This requires configurations in both Slack and GitLab.
-> Note: GitLab can also send events (e.g. issue created) to Slack as notifications. This is the separately configured [Slack Notifications Service](slack.md).
+GitLab can also send events (e.g., `issue created`) to Slack as notifications.
+This is the separately configured [Slack Notifications Service](slack.md).
+
+NOTE: **Note:**
+For GitLab.com, use the [Slack app](gitlab_slack_application.md) instead.
## Configuration
diff --git a/doc/user/project/pipelines/job_artifacts.md b/doc/user/project/pipelines/job_artifacts.md
index a3f40c20192..629b5e1fde4 100644
--- a/doc/user/project/pipelines/job_artifacts.md
+++ b/doc/user/project/pipelines/job_artifacts.md
@@ -55,7 +55,8 @@ For more examples on artifacts, follow the [artifacts reference in
> **Note:**
> With [GitLab 10.1][ce-14399], HTML files in a public project can be previewed
> directly in a new tab without the need to download them when
-> [GitLab Pages](../../../administration/pages/index.md) is enabled
+> [GitLab Pages](../../../administration/pages/index.md) is enabled.
+> The same holds for textual formats (currently supported extensions: `.txt`, `.json`, and `.log`).
After a job finishes, if you visit the job's specific page, there are three
buttons. You can download the artifacts archive or browse its contents, whereas
diff --git a/doc/user/project/repository/index.md b/doc/user/project/repository/index.md
index 718566a539f..97ecc4c0d65 100644
--- a/doc/user/project/repository/index.md
+++ b/doc/user/project/repository/index.md
@@ -97,7 +97,7 @@ Some things to note about precedence:
> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/2508) in GitLab 9.1
-[Jupyter][jupyter] Notebook (previously IPython Notebook) files are used for
+[Jupyter](https://jupyter.org) Notebook (previously IPython Notebook) files are used for
interactive computing in many fields and contain a complete record of the
user's sessions and include code, narrative text, equations and rich output.
@@ -220,14 +220,10 @@ Select branches to compare using the [branch filter search box](branches/index.m
Find it under your project's **Repository > Compare**.
-## Locked files
+## Locked files **[PREMIUM]**
-> Available in [GitLab Premium](https://about.gitlab.com/pricing/).
-
-Lock your files to prevent any conflicting changes.
-
-[File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html) is available only in
-[GitLab Premium](https://about.gitlab.com/pricing/).
+Use [File Locking](https://docs.gitlab.com/ee/user/project/file_lock.html) to
+lock your files to prevent any conflicting changes.
## Repository's API
@@ -243,22 +239,19 @@ used for cloning your project. The button is only shown on macOS.
## Download Source Code
-Source code stored in the repository can be downloaded.
+> Support for directory download was [introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/24704) in GitLab 11.11.
+The source code stored in a repository can be downloaded from the UI.
By clicking the download icon, a dropdown will open with links to download the following:
![Download source code](img/download_source_code.png)
-- **Source Code:**
- This allows users to download the source code on branch they're currently
- viewing. Available zip, tar, tar.gz and tar.bz2.
+- **Source code:**
+ allows users to download the source code on branch they're currently
+ viewing. Available extensions: `zip`, `tar`, `tar.gz`, and `tar.bz2`.
- **Directory:**
- > [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/24704) in GitLab 11.10
-
- Only shows up when viewing a sub-directory. This allows users to download
- the specific directory they're currently viewing. Also available in zip, tar,
- tar.gz and tar.bz2.
+ only shows up when viewing a sub-directory. This allows users to download
+ the specific directory they're currently viewing. Also available in `zip`,
+ `tar`, `tar.gz`, and `tar.bz2`.
- **Artifacts:**
- This allows users to download the artifacts of the latest CI build.
-
-[jupyter]: https://jupyter.org
+ allows users to download the artifacts of the latest CI build.
diff --git a/lib/api/branches.rb b/lib/api/branches.rb
index 5c98b0ad56c..65d7f68bbf9 100644
--- a/lib/api/branches.rb
+++ b/lib/api/branches.rb
@@ -162,8 +162,8 @@ module API
result = DeleteBranchService.new(user_project, current_user)
.execute(params[:branch])
- if result[:status] != :success
- render_api_error!(result[:message], result[:return_code])
+ if result.error?
+ render_api_error!(result.message, result.http_status)
end
end
end
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index 77e4c438bd0..4d67140b0a1 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -82,16 +82,18 @@ module Banzai
def object_link_text(object, matches)
label_suffix = ''
+ parent = project || group
if project || full_path_ref?(matches)
project_path = full_project_path(matches[:namespace], matches[:project])
parent_from_ref = from_ref_cached(project_path)
- reference = parent_from_ref.to_human_reference(project || group)
+ reference = parent_from_ref.to_human_reference(parent)
label_suffix = " <i>in #{reference}</i>" if reference.present?
end
- LabelsHelper.render_colored_label(object, label_suffix: label_suffix, title: tooltip_title(object))
+ presenter = object.present(issuable_subject: parent)
+ LabelsHelper.render_colored_label(presenter, label_suffix: label_suffix, title: tooltip_title(presenter))
end
def tooltip_title(label)
diff --git a/lib/gitlab/graphql/authorize/authorize_field_service.rb b/lib/gitlab/graphql/authorize/authorize_field_service.rb
index 03d6aabb0e3..619ce100421 100644
--- a/lib/gitlab/graphql/authorize/authorize_field_service.rb
+++ b/lib/gitlab/graphql/authorize/authorize_field_service.rb
@@ -48,7 +48,7 @@ module Gitlab
end
def authorize_against(parent_typed_object, resolved_type)
- if built_in_type?
+ if scalar_type?
# The field is a built-in/scalar type, or a list of scalars
# authorize using the parent's object
parent_typed_object.object
@@ -108,8 +108,8 @@ module Gitlab
type.unwrap
end
- def built_in_type?
- GraphQL::Schema::BUILT_IN_TYPES.has_value?(node_type_for_basic_connection(@field.type))
+ def scalar_type?
+ node_type_for_basic_connection(@field.type).kind.scalar?
end
end
end
diff --git a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
index fcf6a25ab00..acb7f225b17 100644
--- a/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
+++ b/lib/gitlab/import_export/after_export_strategies/web_upload_strategy.rb
@@ -30,10 +30,7 @@ module Gitlab
def handle_response_error(response)
unless response.success?
- error_code = response.dig('Error', 'Code') || response.code
- error_message = response.dig('Error', 'Message') || response.message
-
- raise StrategyError.new("Error uploading the project. Code #{error_code}: #{error_message}")
+ raise StrategyError.new("Error uploading the project. Code #{response.code}: #{response.message}")
end
end
diff --git a/lib/quality/seeders/issues.rb b/lib/quality/seeders/issues.rb
new file mode 100644
index 00000000000..4c8cb6e97cc
--- /dev/null
+++ b/lib/quality/seeders/issues.rb
@@ -0,0 +1,58 @@
+# frozen_string_literal: true
+
+# rubocop:disable CodeReuse/ActiveRecord
+module Quality
+ module Seeders
+ class Issues
+ DEFAULT_BACKFILL_WEEKS = 52
+ DEFAULT_AVERAGE_ISSUES_PER_WEEK = 10
+
+ attr_reader :project, :user
+
+ def initialize(project:)
+ @project = project
+ end
+
+ def seed(backfill_weeks: DEFAULT_BACKFILL_WEEKS, average_issues_per_week: DEFAULT_AVERAGE_ISSUES_PER_WEEK)
+ created_at = backfill_weeks.to_i.weeks.ago
+ team = project.team.users
+ created_issues_count = 0
+
+ loop do
+ rand(average_issues_per_week * 2).times do
+ params = {
+ title: FFaker::Lorem.sentence(6),
+ description: FFaker::Lorem.sentence,
+ created_at: created_at + rand(6).days,
+ state: %w[opened closed].sample,
+ milestone: project.milestones.sample,
+ assignee_ids: Array(team.pluck(:id).sample(3)),
+ labels: labels.join(',')
+ }
+ issue = ::Issues::CreateService.new(project, team.sample, params).execute
+
+ if issue.persisted?
+ created_issues_count += 1
+ print '.' # rubocop:disable Rails/Output
+ end
+ end
+
+ created_at += 1.week
+
+ break if created_at > Time.now
+ end
+
+ created_issues_count
+ end
+
+ private
+
+ def labels
+ @labels_pool ||= project.labels.limit(rand(3)).pluck(:title).tap do |labels_array|
+ labels_array.concat(project.group.labels.limit(rand(3)).pluck(:title)) if project.group
+ end
+ end
+ end
+ end
+end
+# rubocop:enable CodeReuse/ActiveRecord
diff --git a/lib/tasks/gitlab/seed.rake b/lib/tasks/gitlab/seed.rake
new file mode 100644
index 00000000000..155ba979b36
--- /dev/null
+++ b/lib/tasks/gitlab/seed.rake
@@ -0,0 +1,34 @@
+namespace :gitlab do
+ namespace :seed do
+ desc "GitLab | Seed | Seeds issues"
+ task :issues, [:project_full_path] => :environment do |t, args|
+ projects =
+ if args.project_full_path
+ project = Project.find_by_full_path(args.project_full_path)
+
+ unless project
+ error_message = "Project '#{args.project_full_path}' does not exist!"
+ potential_projects = Project.search(args.project_full_path)
+
+ if potential_projects.present?
+ error_message += " Did you mean '#{potential_projects.first.full_path}'?"
+ end
+
+ puts error_message.color(:red)
+ exit 1
+ end
+
+ [project]
+ else
+ Project.find_each
+ end
+
+ projects.each do |project|
+ puts "\nSeeding issues for the '#{project.full_path}' project"
+ seeder = Quality::Seeders::Issues.new(project: project)
+ issues_created = seeder.seed(backfill_weeks: 5, average_issues_per_week: 2)
+ puts "\n#{issues_created} issues created!"
+ end
+ end
+ end
+end
diff --git a/package.json b/package.json
index 1cb9f7a9ade..e04470109be 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
"@babel/preset-env": "^7.3.1",
"@gitlab/csslab": "^1.9.0",
"@gitlab/svgs": "^1.59.0",
- "@gitlab/ui": "^3.3.0",
+ "@gitlab/ui": "^3.4.0",
"apollo-cache-inmemory": "^1.5.1",
"apollo-client": "^2.5.1",
"apollo-upload-client": "^10.0.0",
diff --git a/spec/controllers/omniauth_callbacks_controller_spec.rb b/spec/controllers/omniauth_callbacks_controller_spec.rb
index 0af0eb4f942..6e374a8daa7 100644
--- a/spec/controllers/omniauth_callbacks_controller_spec.rb
+++ b/spec/controllers/omniauth_callbacks_controller_spec.rb
@@ -9,10 +9,14 @@ describe OmniauthCallbacksController, type: :controller do
let(:user) { create(:omniauth_user, extern_uid: extern_uid, provider: provider) }
before do
- mock_auth_hash(provider.to_s, +extern_uid, user.email)
+ @original_env_config_omniauth_auth = mock_auth_hash(provider.to_s, +extern_uid, user.email)
stub_omniauth_provider(provider, context: request)
end
+ after do
+ Rails.application.env_config['omniauth.auth'] = @original_env_config_omniauth_auth
+ end
+
context 'when the user is on the last sign in attempt' do
let(:extern_uid) { 'my-uid' }
diff --git a/spec/controllers/projects/settings/ci_cd_controller_spec.rb b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
index fc9a0adeed2..db53e5bc8a4 100644
--- a/spec/controllers/projects/settings/ci_cd_controller_spec.rb
+++ b/spec/controllers/projects/settings/ci_cd_controller_spec.rb
@@ -191,6 +191,15 @@ describe Projects::Settings::CiCdController do
expect(project.build_timeout).to eq(5400)
end
end
+
+ context 'when build_timeout_human_readable is invalid' do
+ let(:params) { { build_timeout_human_readable: '5m' } }
+
+ it 'set specified timeout' do
+ expect(subject).to set_flash[:alert]
+ expect(response).to redirect_to(namespace_project_settings_ci_cd_path)
+ end
+ end
end
end
end
diff --git a/spec/features/dashboard/todos/todos_spec.rb b/spec/features/dashboard/todos/todos_spec.rb
index fd8677feab5..d58e3b2841e 100644
--- a/spec/features/dashboard/todos/todos_spec.rb
+++ b/spec/features/dashboard/todos/todos_spec.rb
@@ -17,6 +17,26 @@ describe 'Dashboard Todos' do
end
end
+ context 'when the todo references a merge request' do
+ let(:referenced_mr) { create(:merge_request, source_project: project) }
+ let(:note) { create(:note, project: project, note: "Check out #{referenced_mr.to_reference}") }
+ let!(:todo) { create(:todo, :mentioned, user: user, project: project, author: author, note: note) }
+
+ before do
+ sign_in(user)
+ visit dashboard_todos_path
+ end
+
+ it 'renders the mr link with the extra attributes' do
+ link = page.find_link(referenced_mr.to_reference)
+
+ expect(link).not_to be_nil
+ expect(link['data-iid']).to eq(referenced_mr.iid.to_s)
+ expect(link['data-project-path']).to eq(referenced_mr.project.full_path)
+ expect(link['data-mr-title']).to eq(referenced_mr.title)
+ end
+ end
+
context 'User has a todo', :js do
before do
create(:todo, :mentioned, user: user, project: project, target: issue, author: author)
diff --git a/spec/features/users/login_spec.rb b/spec/features/users/login_spec.rb
index 9d5780d29b0..efba303033b 100644
--- a/spec/features/users/login_spec.rb
+++ b/spec/features/users/login_spec.rb
@@ -137,7 +137,7 @@ describe 'Login' do
enter_code(user.current_otp)
- expect(page).not_to have_content('You are already signed in.')
+ expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated'))
end
context 'using one-time code' do
@@ -317,7 +317,17 @@ describe 'Login' do
gitlab_sign_in(user)
expect(current_path).to eq root_path
- expect(page).not_to have_content('You are already signed in.')
+ expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated'))
+ end
+
+ it 'does not show already signed in message when opening sign in page after login' do
+ expect(authentication_metrics)
+ .to increment(:user_authenticated_counter)
+
+ gitlab_sign_in(user)
+ visit new_user_session_path
+
+ expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated'))
end
end
@@ -579,7 +589,7 @@ describe 'Login' do
click_button 'Accept terms'
expect(current_path).to eq(root_path)
- expect(page).not_to have_content('You are already signed in.')
+ expect(page).not_to have_content(I18n.t('devise.failure.already_authenticated'))
end
it 'does not ask for terms when the user already accepted them' do
diff --git a/spec/frontend/gfm_auto_complete_spec.js b/spec/frontend/gfm_auto_complete_spec.js
index 841aff0d7ff..fba7c41df94 100644
--- a/spec/frontend/gfm_auto_complete_spec.js
+++ b/spec/frontend/gfm_auto_complete_spec.js
@@ -1,7 +1,7 @@
/* eslint no-param-reassign: "off" */
import $ from 'jquery';
-import GfmAutoComplete from '~/gfm_auto_complete';
+import GfmAutoComplete from 'ee_else_ce/gfm_auto_complete';
import 'jquery.caret';
import 'at.js';
diff --git a/spec/frontend/mr_popover/index_spec.js b/spec/frontend/mr_popover/index_spec.js
index 8c33e52a04b..b9db2342687 100644
--- a/spec/frontend/mr_popover/index_spec.js
+++ b/spec/frontend/mr_popover/index_spec.js
@@ -7,18 +7,28 @@ createDefaultClient.default = jest.fn();
describe('initMRPopovers', () => {
let mr1;
let mr2;
+ let mr3;
beforeEach(() => {
setHTMLFixture(`
- <div id="one" class="gfm-merge_request">MR1</div>
- <div id="two" class="gfm-merge_request">MR2</div>
+ <div id="one" class="gfm-merge_request" data-mr-title="title" data-iid="1" data-project-path="group/project">
+ MR1
+ </div>
+ <div id="two" class="gfm-merge_request" data-mr-title="title" data-iid="1" data-project-path="group/project">
+ MR2
+ </div>
+ <div id="three" class="gfm-merge_request">
+ MR3
+ </div>
`);
mr1 = document.querySelector('#one');
mr2 = document.querySelector('#two');
+ mr3 = document.querySelector('#three');
mr1.addEventListener = jest.fn();
mr2.addEventListener = jest.fn();
+ mr3.addEventListener = jest.fn();
});
it('does not add the same event listener twice', () => {
@@ -27,4 +37,10 @@ describe('initMRPopovers', () => {
expect(mr1.addEventListener).toHaveBeenCalledTimes(1);
expect(mr2.addEventListener).toHaveBeenCalledTimes(1);
});
+
+ it('does not add listener if it does not have the necessary data attributes', () => {
+ initMRPopovers([mr1, mr2, mr3]);
+
+ expect(mr3.addEventListener).not.toHaveBeenCalled();
+ });
});
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index a049b5a6133..58eaf991d6e 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -67,27 +67,29 @@ describe LabelsHelper do
describe 'link_to_label' do
let(:project) { create(:project) }
let(:label) { create(:label, project: project) }
+ let(:subject) { nil }
+ let(:label_presenter) { label.present(issuable_subject: subject) }
context 'without subject' do
it "uses the label's project" do
- expect(link_to_label(label)).to match %r{<a href="/#{label.project.full_path}/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter)).to match %r{<a href="/#{label.project.full_path}/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
context 'with a project as subject' do
let(:namespace) { build(:namespace, name: 'foo3') }
- let(:another_project) { build(:project, namespace: namespace, name: 'bar3') }
+ let(:subject) { build(:project, namespace: namespace, name: 'bar3') }
it 'links to project issues page' do
- expect(link_to_label(label, subject: another_project)).to match %r{<a href="/foo3/bar3/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter)).to match %r{<a href="/foo3/bar3/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
context 'with a group as subject' do
- let(:group) { build(:group, name: 'bar') }
+ let(:subject) { build(:group, name: 'bar') }
it 'links to group issues page' do
- expect(link_to_label(label, subject: group)).to match %r{<a href="/groups/bar/-/issues\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter)).to match %r{<a href="/groups/bar/-/issues\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
@@ -95,7 +97,7 @@ describe LabelsHelper do
['issue', :issue, 'merge_request', :merge_request].each do |type|
context "set to #{type}" do
it 'links to correct page' do
- expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.full_path}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
+ expect(link_to_label(label_presenter, type: type)).to match %r{<a href="/#{label.project.full_path}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>}
end
end
end
@@ -104,14 +106,14 @@ describe LabelsHelper do
context 'with a tooltip argument' do
context 'set to false' do
it 'does not include the has-tooltip class' do
- expect(link_to_label(label, tooltip: false)).not_to match /has-tooltip/
+ expect(link_to_label(label_presenter, tooltip: false)).not_to match /has-tooltip/
end
end
end
context 'with block' do
it 'passes the block to link_to' do
- link = link_to_label(label) { 'Foo' }
+ link = link_to_label(label_presenter) { 'Foo' }
expect(link).to match('Foo')
end
end
@@ -119,8 +121,8 @@ describe LabelsHelper do
context 'without block' do
it 'uses render_colored_label as the link content' do
expect(self).to receive(:render_colored_label)
- .with(label, tooltip: true).and_return('Foo')
- expect(link_to_label(label)).to match('Foo')
+ .with(label_presenter, tooltip: true).and_return('Foo')
+ expect(link_to_label(label_presenter)).to match('Foo')
end
end
end
@@ -237,16 +239,24 @@ describe LabelsHelper do
end
end
- describe 'labels_sorted_by_title' do
+ describe 'presented_labels_sorted_by_title' do
+ let(:labels) do
+ [build(:label, title: 'a'),
+ build(:label, title: 'B'),
+ build(:label, title: 'c'),
+ build(:label, title: 'D')]
+ end
+
it 'sorts labels alphabetically' do
- label1 = double(:label, title: 'a')
- label2 = double(:label, title: 'B')
- label3 = double(:label, title: 'c')
- label4 = double(:label, title: 'D')
- labels = [label1, label2, label3, label4]
-
- expect(labels_sorted_by_title(labels))
- .to match_array([label2, label4, label1, label3])
+ sorted_ids = presented_labels_sorted_by_title(labels, nil).map(&:id)
+
+ expect(sorted_ids)
+ .to match_array([labels[1].id, labels[3].id, labels[0].id, labels[2].id])
+ end
+
+ it 'returns an array of label presenters' do
+ expect(presented_labels_sorted_by_title(labels, nil))
+ .to all(be_a(LabelPresenter))
end
end
diff --git a/spec/javascripts/ide/stores/modules/commit/actions_spec.js b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
index abc97f3072c..cdeb9b4b896 100644
--- a/spec/javascripts/ide/stores/modules/commit/actions_spec.js
+++ b/spec/javascripts/ide/stores/modules/commit/actions_spec.js
@@ -4,6 +4,7 @@ import service from '~/ide/services';
import router from '~/ide/ide_router';
import eventHub from '~/ide/eventhub';
import consts from '~/ide/stores/modules/commit/constants';
+import { commitActionTypes } from '~/ide/constants';
import { resetStore, file } from 'spec/ide/helpers';
describe('IDE commit module actions', () => {
@@ -294,7 +295,7 @@ describe('IDE commit module actions', () => {
commit_message: 'testing 123',
actions: [
{
- action: 'update',
+ action: commitActionTypes.update,
file_path: jasmine.anything(),
content: undefined,
encoding: jasmine.anything(),
@@ -321,7 +322,7 @@ describe('IDE commit module actions', () => {
commit_message: 'testing 123',
actions: [
{
- action: 'update',
+ action: commitActionTypes.update,
file_path: jasmine.anything(),
content: undefined,
encoding: jasmine.anything(),
diff --git a/spec/javascripts/ide/stores/utils_spec.js b/spec/javascripts/ide/stores/utils_spec.js
index c4f122efdda..debe1c4acee 100644
--- a/spec/javascripts/ide/stores/utils_spec.js
+++ b/spec/javascripts/ide/stores/utils_spec.js
@@ -1,4 +1,5 @@
import * as utils from '~/ide/stores/utils';
+import { commitActionTypes } from '~/ide/constants';
import { file } from '../helpers';
describe('Multi-file store utils', () => {
@@ -107,7 +108,7 @@ describe('Multi-file store utils', () => {
commit_message: 'commit message',
actions: [
{
- action: 'update',
+ action: commitActionTypes.update,
file_path: 'staged',
content: 'updated file content',
encoding: 'text',
@@ -115,7 +116,7 @@ describe('Multi-file store utils', () => {
previous_path: undefined,
},
{
- action: 'create',
+ action: commitActionTypes.create,
file_path: 'added',
content: 'new file content',
encoding: 'base64',
@@ -123,7 +124,7 @@ describe('Multi-file store utils', () => {
previous_path: undefined,
},
{
- action: 'delete',
+ action: commitActionTypes.delete,
file_path: 'deletedFile',
content: undefined,
encoding: 'text',
@@ -170,7 +171,7 @@ describe('Multi-file store utils', () => {
commit_message: 'prebuilt test commit message',
actions: [
{
- action: 'update',
+ action: commitActionTypes.update,
file_path: 'staged',
content: 'updated file content',
encoding: 'text',
@@ -178,7 +179,7 @@ describe('Multi-file store utils', () => {
previous_path: undefined,
},
{
- action: 'create',
+ action: commitActionTypes.create,
file_path: 'added',
content: 'new file content',
encoding: 'base64',
@@ -193,19 +194,19 @@ describe('Multi-file store utils', () => {
describe('commitActionForFile', () => {
it('returns deleted for deleted file', () => {
- expect(utils.commitActionForFile({ deleted: true })).toBe('delete');
+ expect(utils.commitActionForFile({ deleted: true })).toBe(commitActionTypes.delete);
});
it('returns create for tempFile', () => {
- expect(utils.commitActionForFile({ tempFile: true })).toBe('create');
+ expect(utils.commitActionForFile({ tempFile: true })).toBe(commitActionTypes.create);
});
it('returns move for moved file', () => {
- expect(utils.commitActionForFile({ prevPath: 'test' })).toBe('move');
+ expect(utils.commitActionForFile({ prevPath: 'test' })).toBe(commitActionTypes.move);
});
it('returns update by default', () => {
- expect(utils.commitActionForFile({})).toBe('update');
+ expect(utils.commitActionForFile({})).toBe(commitActionTypes.update);
});
});
diff --git a/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb b/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
index 95a4eb296fb..aec9c4baf0a 100644
--- a/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
+++ b/spec/lib/gitlab/graphql/authorize/authorize_field_service_spec.rb
@@ -45,7 +45,7 @@ describe Gitlab::Graphql::Authorize::AuthorizeFieldService do
end
end
- context "when the field is a scalar type" do
+ context "when the field is a built-in scalar type" do
let(:field) { type_with_field(GraphQL::STRING_TYPE, :read_field).fields["testField"].to_graphql }
let(:expected_permissions) { [:read_field] }
@@ -58,6 +58,20 @@ describe Gitlab::Graphql::Authorize::AuthorizeFieldService do
it_behaves_like "checking permissions on the presented object"
end
+
+ context "when the field is sub-classed scalar type" do
+ let(:field) { type_with_field(Types::TimeType, :read_field).fields["testField"].to_graphql }
+ let(:expected_permissions) { [:read_field] }
+
+ it_behaves_like "checking permissions on the presented object"
+ end
+
+ context "when the field is a list of sub-classed scalar types" do
+ let(:field) { type_with_field([Types::TimeType], :read_field).fields["testField"].to_graphql }
+ let(:expected_permissions) { [:read_field] }
+
+ it_behaves_like "checking permissions on the presented object"
+ end
end
context "when the field is a specific type" do
diff --git a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
index ec17ad8541f..7c4ac62790e 100644
--- a/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
+++ b/spec/lib/gitlab/import_export/after_export_strategies/web_upload_strategy_spec.rb
@@ -32,5 +32,17 @@ describe Gitlab::ImportExport::AfterExportStrategies::WebUploadStrategy do
strategy.execute(user, project)
end
+
+ context 'when upload fails' do
+ it 'stores the export error' do
+ stub_request(:post, example_url).to_return(status: [404, 'Page not found'])
+
+ strategy.execute(user, project)
+
+ errors = project.import_export_shared.errors
+ expect(errors).not_to be_empty
+ expect(errors.first).to eq "Error uploading the project. Code 404: Page not found"
+ end
+ end
end
end
diff --git a/spec/lib/quality/seeders/issues_spec.rb b/spec/lib/quality/seeders/issues_spec.rb
new file mode 100644
index 00000000000..e17414a541a
--- /dev/null
+++ b/spec/lib/quality/seeders/issues_spec.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Quality::Seeders::Issues do
+ let(:project) { create(:project) }
+
+ subject { described_class.new(project: project) }
+
+ describe '#seed' do
+ it 'seeds issues' do
+ issues_created = subject.seed(backfill_weeks: 1, average_issues_per_week: 1)
+
+ expect(issues_created).to be_between(0, 2)
+ expect(project.issues.count).to eq(issues_created)
+ end
+ end
+end
diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb
index aacfbe3f180..44b5af5e5aa 100644
--- a/spec/models/ci/bridge_spec.rb
+++ b/spec/models/ci/bridge_spec.rb
@@ -33,7 +33,7 @@ describe Ci::Bridge do
CI_PROJECT_ID CI_PROJECT_NAME CI_PROJECT_PATH
CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PIPELINE_IID
CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE
- CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION
+ CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION CI_COMMIT_REF_PROTECTED
]
expect(bridge.scoped_variables_hash.keys).to include(*variables)
diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb
index 95b9bd4a4d9..3a7d20a58c8 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/ci/build_spec.rb
@@ -2183,7 +2183,8 @@ describe Ci::Build do
{ key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true, masked: false },
{ key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true, masked: false },
{ key: 'CI_COMMIT_TITLE', value: pipeline.git_commit_title, public: true, masked: false },
- { key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true, masked: false }
+ { key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true, masked: false },
+ { key: 'CI_COMMIT_REF_PROTECTED', value: (!!pipeline.protected_ref?).to_s, public: true, masked: false }
]
end
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb
index fd66e344d63..3c823b78be7 100644
--- a/spec/models/ci/pipeline_spec.rb
+++ b/spec/models/ci/pipeline_spec.rb
@@ -690,7 +690,8 @@ describe Ci::Pipeline, :mailer do
CI_PIPELINE_SOURCE
CI_COMMIT_MESSAGE
CI_COMMIT_TITLE
- CI_COMMIT_DESCRIPTION]
+ CI_COMMIT_DESCRIPTION
+ CI_COMMIT_REF_PROTECTED]
end
context 'when source is merge request' do
diff --git a/spec/models/internal_id_spec.rb b/spec/models/internal_id_spec.rb
index 0ed4e146caa..806b4f61bd8 100644
--- a/spec/models/internal_id_spec.rb
+++ b/spec/models/internal_id_spec.rb
@@ -93,7 +93,7 @@ describe InternalId do
before do
described_class.reset_column_information
# Project factory will also call the current_version
- expect(ActiveRecord::Migrator).to receive(:current_version).twice.and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
+ expect(ActiveRecord::Migrator).to receive(:current_version).at_least(:once).and_return(InternalId::REQUIRED_SCHEMA_VERSION - 1)
end
let(:init) { double('block') }
@@ -104,6 +104,15 @@ describe InternalId do
expect(init).to receive(:call).with(issue).and_return(val)
expect(subject).to eq(val + 1)
end
+
+ it 'always attempts to generate internal IDs in production mode' do
+ allow(Rails.env).to receive(:test?).and_return(false)
+ val = rand(1..100)
+ generator = double(generate: val)
+ expect(InternalId::InternalIdGenerator).to receive(:new).and_return(generator)
+
+ expect(subject).to eq(val)
+ end
end
end
diff --git a/spec/presenters/label_presenter_spec.rb b/spec/presenters/label_presenter_spec.rb
new file mode 100644
index 00000000000..fae8188670f
--- /dev/null
+++ b/spec/presenters/label_presenter_spec.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+describe LabelPresenter do
+ include Gitlab::Routing.url_helpers
+
+ set(:group) { create(:group) }
+ set(:project) { create(:project, group: group) }
+ let(:label) { build_stubbed(:label, project: project).present(issuable_subject: project) }
+ let(:group_label) { build_stubbed(:group_label, group: group).present(issuable_subject: project) }
+
+ describe '#edit_path' do
+ context 'with group label' do
+ subject { group_label.edit_path }
+
+ it { is_expected.to eq(edit_group_label_path(group, group_label)) }
+ end
+
+ context 'with project label' do
+ subject { label.edit_path }
+
+ it { is_expected.to eq(edit_project_label_path(project, label)) }
+ end
+ end
+
+ describe '#destroy_path' do
+ context 'with group label' do
+ subject { group_label.destroy_path }
+
+ it { is_expected.to eq(group_label_path(group, group_label)) }
+ end
+
+ context 'with project label' do
+ subject { label.destroy_path }
+
+ it { is_expected.to eq(project_label_path(project, label)) }
+ end
+ end
+
+ describe '#filter_path' do
+ context 'with group as context subject' do
+ let(:label_in_group) { build_stubbed(:label, project: project).present(issuable_subject: group) }
+ subject { label_in_group.filter_path }
+
+ it { is_expected.to eq(issues_group_path(group, label_name: [label_in_group.title])) }
+ end
+
+ context 'with project as context subject' do
+ subject { label.filter_path }
+
+ it { is_expected.to eq(namespace_project_issues_path(group, project, label_name: [label.title])) }
+ end
+ end
+
+ describe '#can_subscribe_to_label_in_different_levels?' do
+ it 'returns true for group labels in project context' do
+ expect(group_label.can_subscribe_to_label_in_different_levels?).to be_truthy
+ end
+
+ it 'returns false for project labels in project context' do
+ expect(label.can_subscribe_to_label_in_different_levels?).to be_falsey
+ end
+ end
+end
diff --git a/spec/services/delete_branch_service_spec.rb b/spec/services/delete_branch_service_spec.rb
index 43a70d33d2d..b8064c2cbc1 100644
--- a/spec/services/delete_branch_service_spec.rb
+++ b/spec/services/delete_branch_service_spec.rb
@@ -8,20 +8,24 @@ describe DeleteBranchService do
let(:user) { create(:user) }
let(:service) { described_class.new(project, user) }
+ shared_examples 'a deleted branch' do |branch_name|
+ it 'removes the branch' do
+ expect(branch_exists?(branch_name)).to be true
+
+ result = service.execute(branch_name)
+
+ expect(result.status).to eq :success
+ expect(branch_exists?(branch_name)).to be false
+ end
+ end
+
describe '#execute' do
context 'when user has access to push to repository' do
before do
project.add_developer(user)
end
- it 'removes the branch' do
- expect(branch_exists?('feature')).to be true
-
- result = service.execute('feature')
-
- expect(result[:status]).to eq :success
- expect(branch_exists?('feature')).to be false
- end
+ it_behaves_like 'a deleted branch', 'feature'
end
context 'when user does not have access to push to repository' do
@@ -30,8 +34,8 @@ describe DeleteBranchService do
result = service.execute('feature')
- expect(result[:status]).to eq :error
- expect(result[:message]).to eq 'You dont have push access to repo'
+ expect(result.status).to eq :error
+ expect(result.message).to eq 'You dont have push access to repo'
expect(branch_exists?('feature')).to be true
end
end
diff --git a/spec/services/service_response_spec.rb b/spec/services/service_response_spec.rb
new file mode 100644
index 00000000000..30bd4d6820b
--- /dev/null
+++ b/spec/services/service_response_spec.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+require 'fast_spec_helper'
+
+ActiveSupport::Dependencies.autoload_paths << 'app/services'
+
+describe ServiceResponse do
+ describe '.success' do
+ it 'creates a successful response without a message' do
+ expect(described_class.success).to be_success
+ end
+
+ it 'creates a successful response with a message' do
+ response = described_class.success(message: 'Good orange')
+
+ expect(response).to be_success
+ expect(response.message).to eq('Good orange')
+ end
+ end
+
+ describe '.error' do
+ it 'creates a failed response without HTTP status' do
+ response = described_class.error(message: 'Bad apple')
+
+ expect(response).to be_error
+ expect(response.message).to eq('Bad apple')
+ end
+
+ it 'creates a failed response with HTTP status' do
+ response = described_class.error(message: 'Bad apple', http_status: 400)
+
+ expect(response).to be_error
+ expect(response.message).to eq('Bad apple')
+ expect(response.http_status).to eq(400)
+ end
+ end
+
+ describe '#success?' do
+ it 'returns true for a successful response' do
+ expect(described_class.success.success?).to eq(true)
+ end
+
+ it 'returns false for a failed response' do
+ expect(described_class.error(message: 'Bad apple').success?).to eq(false)
+ end
+ end
+
+ describe '#error?' do
+ it 'returns false for a successful response' do
+ expect(described_class.success.error?).to eq(false)
+ end
+
+ it 'returns true for a failed response' do
+ expect(described_class.error(message: 'Bad apple').error?).to eq(true)
+ end
+ end
+end
diff --git a/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
index 72912ffb89d..a0c77eecb61 100644
--- a/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
+++ b/spec/support/controllers/ldap_omniauth_callbacks_controller_shared_context.rb
@@ -25,9 +25,13 @@ shared_context 'Ldap::OmniauthCallbacksController' do
described_class.define_providers!
Rails.application.reload_routes!
- mock_auth_hash(provider.to_s, uid, user.email)
+ @original_env_config_omniauth_auth = mock_auth_hash(provider.to_s, uid, user.email)
stub_omniauth_provider(provider, context: request)
allow(Gitlab::Auth::LDAP::Access).to receive(:allowed?).and_return(valid_login?)
end
+
+ after do
+ Rails.application.env_config['omniauth.auth'] = @original_env_config_omniauth_auth
+ end
end
diff --git a/spec/support/helpers/login_helpers.rb b/spec/support/helpers/login_helpers.rb
index 4a0cf62a661..0bb2d2510c2 100644
--- a/spec/support/helpers/login_helpers.rb
+++ b/spec/support/helpers/login_helpers.rb
@@ -118,7 +118,10 @@ module LoginHelpers
response_object: response_object
}
})
+ original_env_config_omniauth_auth = Rails.application.env_config['omniauth.auth']
Rails.application.env_config['omniauth.auth'] = OmniAuth.config.mock_auth[provider.to_sym]
+
+ original_env_config_omniauth_auth
end
def saml_xml(raw_saml_response)
diff --git a/yarn.lock b/yarn.lock
index ebbb670db0c..8a23aabba20 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -663,10 +663,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.59.0.tgz#affcf9596d736836d37469bb4aea2226ac03e087"
integrity sha512-dokGyyLRRsoBKO70KP1g+ZsDGyTK/RIHWDmvWI6Bx5AxQ3UqAzVXn2OIb3owjJAexyRG1uBmJrriiVVyHznQ4g==
-"@gitlab/ui@^3.3.0":
- version "3.3.0"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-3.3.0.tgz#32112c8eb53a0fd893f8fa431a02ceaf55279323"
- integrity sha512-VVInZmcAe0L0lRMb6II3ED4DYm4OpzSmcxdwt18CqpDMw3EEoUqxd58EAxBFy70isgFTd6cHYhyS8rw5GOULyg==
+"@gitlab/ui@^3.4.0":
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-3.4.0.tgz#174681f210eb16c3d101a36968d5e4d163c0d014"
+ integrity sha512-joXNz80IHMQxEGrqcNUTEKofjfZtkOKUe34HAFI71NEeYT6H0r/lYmJ5Gcz+MmwM1CvZOVbB3DnKzxQPDbN/hQ==
dependencies:
"@babel/standalone" "^7.0.0"
bootstrap-vue "^2.0.0-rc.11"