Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-10-06 12:11:40 +00:00
parent b02d831a00
commit 0d1b0d5d03
58 changed files with 623 additions and 277 deletions

View File

@ -24,26 +24,6 @@ Are there any other stages or teams involved that need to be kept in the loop?
- The Delivery Team
-->
## The Rollout Plan
- Partial Rollout on GitLab.com with testing groups
- Rollout on GitLab.com for a certain period (How long)
- Percentage Rollout on GitLab.com
- Rollout Feature for everyone as soon as it's ready
<!-- Which dashboards from https://dashboards.gitlab.net are most relevant? Sentry errors reports can also be useful to review -->
## Testing Groups/Projects/Users
<!-- If applicable, any groups/projects that are happy to have this feature turned on early. Some organizations may wish to test big changes they are interested in with a small subset of users ahead of time for example. -->
- `gitlab-org/gitlab` project
- `gitlab-org/gitlab-foss` project
- `gitlab-com/www-gitlab-com` project
- `gitlab-org`/`gitlab-com` groups
- ...
## Expectations
### What are we expecting to happen?
@ -62,17 +42,30 @@ Are there any other stages or teams involved that need to be kept in the loop?
### Rollout on non-production environments
- [ ] Ensure that the feature MRs have been deployed to non-production environments.
- Ensure that the feature MRs have been deployed to non-production environments.
- [ ] `/chatops run auto_deploy status <merge-commit-of-your-feature>`
- [ ] Enable the feature globally on non-production environments.
- [ ] `/chatops run feature set <feature-flag-name> true --dev`
- [ ] `/chatops run feature set <feature-flag-name> true --staging`
- [ ] Verify that the feature works as expected. Posting the QA result in this issue is preferable.
### Preparation before production rollout
### Specific rollout on production
- [ ] Ensure that the feature MRs have been deployed to both production and canary.
- Ensure that the feature MRs have been deployed to both production and canary.
- [ ] `/chatops run auto_deploy status <merge-commit-of-your-feature>`
- If you're using [project-actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), you must enable the feature on these entries:
- [ ] `/chatops run feature set --project=gitlab-org/gitlab <feature-flag-name> true`
- [ ] `/chatops run feature set --project=gitlab-org/gitlab-foss <feature-flag-name> true`
- [ ] `/chatops run feature set --project=gitlab-com/www-gitlab-com <feature-flag-name> true`
- If you're using [group-actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), you must enable the feature on these entries:
- [ ] `/chatops run feature set --group=gitlab-org <feature-flag-name> true`
- [ ] `/chatops run feature set --group=gitlab-com <feature-flag-name> true`
- If you're using [user-actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), you must enable the feature on these entries:
- [ ] `/chatops run feature set --user=<your-username> <feature-flag-name> true`
- [ ] Verify that the feature works on the specific entries. Posting the QA result in this issue is preferable.
### Preparation before global rollout
- [ ] Check if the feature flag change needs to be accompanied with a
[change management issue](https://about.gitlab.com/handbook/engineering/infrastructure/change-management/#feature-flags-and-the-change-management-process).
Cross link the issue here if it does.
@ -86,19 +79,13 @@ Are there any other stages or teams involved that need to be kept in the loop?
All `/chatops` commands that target production should be done in the `#production` slack channel for visibility.
- [ ] Confirm the feature flag is enabled on `staging` without incident
- [ ] Roll out the feature to targeted testing projects/groups first
- [ ] `/chatops run feature set --project=gitlab-org/gitlab <feature-flag-name> true`
- [ ] `/chatops run feature set --project=gitlab-org/gitlab-foss <feature-flag-name> true`
- [ ] `/chatops run feature set --project=gitlab-com/www-gitlab-com <feature-flag-name> true`
- [ ] [Incrementally roll out](https://docs.gitlab.com/ee/development/feature_flags/controls.html#process) the feature.
- If the feature flag in code has [an actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), perform **actor-based** rollout.
- [ ] `/chatops run feature set <feature-flag-name> <rollout-percentage> --actors`
- If the feature flag in code does **NOT** have [an actor](https://docs.gitlab.com/ee/development/feature_flags/#feature-actors), perform time-based rollout (**random** rollout).
- [ ] `/chatops run feature set <feature-flag-name> <rollout-percentage>`
- [ ] Verify the change has the desired outcome with the limited rollout before enabling the feature globally on production.
- [ ] Enable the feature globally on production environment. `/chatops run feature set <feature-flag-name> true`
- Enable the feature globally on production environment.
- [ ] `/chatops run feature set <feature-flag-name> true`
- [ ] Announce on [the feature issue](ISSUE LINK) that the feature has been globally enabled.
- [ ] Wait for [at least one day for the verification term](https://about.gitlab.com/handbook/product-development-flow/feature-flag-lifecycle/#including-a-feature-behind-feature-flag-in-the-final-release).

View File

@ -1 +1 @@
a47a975ef7d4ef51e0d68c5662d5cb3bb5b83b76
2b424740fc73419f40bd74c2f05db3d7ef774198

View File

@ -205,7 +205,7 @@ export default {
:disabled="isSubmitButtonDisabled"
:loading="isSubmitting"
type="submit"
class="js-add-issuable-form-add-button float-left"
class="float-left"
data-qa-selector="add_issue_button"
>
{{ __('Add') }}

View File

@ -111,7 +111,6 @@ export default {
:disabled="removeDisabled"
data-testid="removeBtn"
type="button"
class="js-issue-token-remove-button"
@click="onRemoveRequest"
>
<gl-icon name="close" />

View File

@ -107,9 +107,6 @@ export default {
onAutoCompleteToggled(isOpen) {
this.isAutoCompleteOpen = isOpen;
},
onInputWrapperClick() {
this.$refs.input.focus();
},
onInput() {
const { value } = this.$refs.input;
const caretPos = this.$refs.input.selectionStart;
@ -185,26 +182,23 @@ export default {
<div
ref="issuableFormWrapper"
:class="{ focus: isInputFocused }"
class="add-issuable-form-input-wrapper form-control gl-field-error-outline"
class="add-issuable-form-input-wrapper form-control gl-field-error-outline gl-h-auto gl-p-3 gl-pb-2"
role="button"
@click="onIssuableFormWrapperClick"
>
<ul class="add-issuable-form-input-token-list">
<!--
We need to ensure this key changes any time the pendingReferences array is updated
else two consecutive pending ref strings in an array with the same name will collide
and cause odd behavior when one is removed.
-->
<ul
class="gl-display-flex gl-flex-wrap gl-align-items-baseline gl-list-style-none gl-m-0 gl-p-0"
>
<li
v-for="(reference, index) in references"
:key="`related-issues-token-${reference}`"
class="js-add-issuable-form-token-list-item add-issuable-form-token-list-item"
:key="reference"
class="gl-max-w-full gl-mb-2 gl-mr-2"
>
<issue-token
:id-key="index"
:display-reference="reference.text || reference"
:can-remove="true"
:is-condensed="true"
can-remove
is-condensed
:path-id-separator="pathIdSeparator"
event-namespace="pendingIssuable"
@pendingIssuableRemoveRequest="
@ -214,14 +208,15 @@ export default {
"
/>
</li>
<li class="add-issuable-form-input-list-item">
<li class="gl-mb-2 gl-flex-grow-1">
<input
:id="inputId"
ref="input"
:value="inputValue"
:placeholder="inputPlaceholder"
:aria-label="inputPlaceholder"
type="text"
class="js-add-issuable-form-input add-issuable-form-input"
class="gl-w-full gl-border-none gl-outline-0"
data-qa-selector="add_issue_field"
autocomplete="off"
@input="onInput"

View File

@ -1,5 +1,5 @@
<script>
import { GlIcon, GlLink, GlLoadingIcon } from '@gitlab/ui';
import { GlIcon, GlLink, GlLoadingIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import $ from 'jquery';
import '~/behaviors/markdown/render_gfm';
import { handleLocationHash } from '~/lib/utils/common_utils';
@ -22,6 +22,9 @@ export default {
GlLink,
GlLoadingIcon,
},
directives: {
SafeHtml,
},
props: {
blob: {
type: Object,
@ -59,11 +62,7 @@ export default {
</div>
<div class="blob-viewer" data-qa-selector="blob_viewer_content" itemprop="about">
<gl-loading-icon v-if="loading > 0" size="md" color="dark" class="my-4 mx-auto" />
<div
v-else-if="readme"
ref="readme"
v-html="readme.html /* eslint-disable-line vue/no-v-html */"
></div>
<div v-else-if="readme" ref="readme" v-safe-html="readme.html"></div>
</div>
</article>
</template>

View File

@ -1,11 +1,18 @@
<script>
import { GlBadge } from '@gitlab/ui';
import { GlTooltipDirective } from '@gitlab/ui';
import RunnerTypeBadge from '../runner_type_badge.vue';
import RunnerStateLockedBadge from '../runner_state_locked_badge.vue';
import RunnerStatePausedBadge from '../runner_state_paused_badge.vue';
import { I18N_LOCKED_RUNNER_DESCRIPTION, I18N_PAUSED_RUNNER_DESCRIPTION } from '../../constants';
export default {
components: {
GlBadge,
RunnerTypeBadge,
RunnerStateLockedBadge,
RunnerStatePausedBadge,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
runner: {
@ -24,19 +31,17 @@ export default {
return !this.runner.active;
},
},
i18n: {
I18N_LOCKED_RUNNER_DESCRIPTION,
I18N_PAUSED_RUNNER_DESCRIPTION,
},
};
</script>
<template>
<div>
<runner-type-badge :type="runnerType" size="sm" />
<gl-badge v-if="locked" variant="warning" size="sm">
{{ s__('Runners|locked') }}
</gl-badge>
<gl-badge v-if="paused" variant="danger" size="sm">
{{ s__('Runners|paused') }}
</gl-badge>
<runner-state-locked-badge v-if="locked" size="sm" />
<runner-state-paused-badge v-if="paused" size="sm" />
</div>
</template>

View File

@ -0,0 +1,25 @@
<script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { I18N_LOCKED_RUNNER_DESCRIPTION } from '../constants';
export default {
components: {
GlBadge,
},
directives: {
GlTooltip: GlTooltipDirective,
},
i18n: {
I18N_LOCKED_RUNNER_DESCRIPTION,
},
};
</script>
<template>
<gl-badge
v-gl-tooltip="$options.i18n.I18N_LOCKED_RUNNER_DESCRIPTION"
variant="warning"
v-bind="$attrs"
>
{{ s__('Runners|locked') }}
</gl-badge>
</template>

View File

@ -0,0 +1,25 @@
<script>
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { I18N_PAUSED_RUNNER_DESCRIPTION } from '../constants';
export default {
components: {
GlBadge,
},
directives: {
GlTooltip: GlTooltipDirective,
},
i18n: {
I18N_PAUSED_RUNNER_DESCRIPTION,
},
};
</script>
<template>
<gl-badge
v-gl-tooltip="$options.i18n.I18N_PAUSED_RUNNER_DESCRIPTION"
variant="danger"
v-bind="$attrs"
>
{{ s__('Runners|paused') }}
</gl-badge>
</template>

View File

@ -1,20 +1,30 @@
<script>
import { GlBadge } from '@gitlab/ui';
import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '../constants';
import {
INSTANCE_TYPE,
GROUP_TYPE,
PROJECT_TYPE,
I18N_INSTANCE_RUNNER_DESCRIPTION,
I18N_GROUP_RUNNER_DESCRIPTION,
I18N_PROJECT_RUNNER_DESCRIPTION,
} from '../constants';
const BADGE_DATA = {
[INSTANCE_TYPE]: {
variant: 'success',
text: s__('Runners|shared'),
tooltip: I18N_INSTANCE_RUNNER_DESCRIPTION,
},
[GROUP_TYPE]: {
variant: 'success',
text: s__('Runners|group'),
tooltip: I18N_GROUP_RUNNER_DESCRIPTION,
},
[PROJECT_TYPE]: {
variant: 'info',
text: s__('Runners|specific'),
tooltip: I18N_PROJECT_RUNNER_DESCRIPTION,
},
};
@ -22,6 +32,9 @@ export default {
components: {
GlBadge,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
type: {
type: String,
@ -40,7 +53,7 @@ export default {
};
</script>
<template>
<gl-badge v-if="badge" :variant="badge.variant" v-bind="$attrs">
<gl-badge v-if="badge" v-gl-tooltip="badge.tooltip" :variant="badge.variant" v-bind="$attrs">
{{ badge.text }}
</gl-badge>
</template>

View File

@ -1,18 +1,36 @@
<script>
import { GlBadge } from '@gitlab/ui';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '../constants';
import {
INSTANCE_TYPE,
GROUP_TYPE,
PROJECT_TYPE,
I18N_INSTANCE_RUNNER_DESCRIPTION,
I18N_GROUP_RUNNER_DESCRIPTION,
I18N_PROJECT_RUNNER_DESCRIPTION,
I18N_LOCKED_RUNNER_DESCRIPTION,
I18N_PAUSED_RUNNER_DESCRIPTION,
} from '../constants';
import RunnerTypeBadge from './runner_type_badge.vue';
import RunnerStateLockedBadge from './runner_state_locked_badge.vue';
import RunnerStatePausedBadge from './runner_state_paused_badge.vue';
export default {
components: {
GlBadge,
RunnerTypeBadge,
RunnerStateLockedBadge,
RunnerStatePausedBadge,
},
runnerTypes: {
INSTANCE_TYPE,
GROUP_TYPE,
PROJECT_TYPE,
},
i18n: {
I18N_INSTANCE_RUNNER_DESCRIPTION,
I18N_GROUP_RUNNER_DESCRIPTION,
I18N_PROJECT_RUNNER_DESCRIPTION,
I18N_LOCKED_RUNNER_DESCRIPTION,
I18N_PAUSED_RUNNER_DESCRIPTION,
},
};
</script>
@ -32,27 +50,23 @@ export default {
<ul>
<li>
<runner-type-badge :type="$options.runnerTypes.INSTANCE_TYPE" size="sm" />
- {{ __('Runs jobs from all unassigned projects.') }}
- {{ $options.i18n.I18N_INSTANCE_RUNNER_DESCRIPTION }}
</li>
<li>
<runner-type-badge :type="$options.runnerTypes.GROUP_TYPE" size="sm" />
- {{ __('Runs jobs from all unassigned projects in its group.') }}
- {{ $options.i18n.I18N_GROUP_RUNNER_DESCRIPTION }}
</li>
<li>
<runner-type-badge :type="$options.runnerTypes.PROJECT_TYPE" size="sm" />
- {{ __('Runs jobs from assigned projects.') }}
- {{ $options.i18n.I18N_PROJECT_RUNNER_DESCRIPTION }}
</li>
<li>
<gl-badge variant="warning" size="sm">
{{ s__('Runners|locked') }}
</gl-badge>
- {{ __('Cannot be assigned to other projects.') }}
<runner-state-locked-badge size="sm" />
- {{ $options.i18n.I18N_LOCKED_RUNNER_DESCRIPTION }}
</li>
<li>
<gl-badge variant="danger" size="sm">
{{ s__('Runners|paused') }}
</gl-badge>
- {{ __('Not available to run jobs.') }}
<runner-state-paused-badge size="sm" />
- {{ $options.i18n.I18N_PAUSED_RUNNER_DESCRIPTION }}
</li>
</ul>
</div>

View File

@ -7,6 +7,14 @@ export const GROUP_RUNNER_COUNT_LIMIT = 1000;
export const I18N_FETCH_ERROR = s__('Runners|Something went wrong while fetching runner data.');
export const I18N_DETAILS_TITLE = s__('Runners|Runner #%{runner_id}');
export const I18N_INSTANCE_RUNNER_DESCRIPTION = s__('Runners|Available to all projects');
export const I18N_GROUP_RUNNER_DESCRIPTION = s__(
'Runners|Available to all projects and subgroups in the group',
);
export const I18N_PROJECT_RUNNER_DESCRIPTION = s__('Runners|Associated with one or more projects');
export const I18N_LOCKED_RUNNER_DESCRIPTION = s__('Runners|You cannot assign to other projects');
export const I18N_PAUSED_RUNNER_DESCRIPTION = s__('Runners|Not available to run jobs');
export const RUNNER_TAG_BADGE_VARIANT = 'info';
export const RUNNER_TAG_BG_CLASS = 'gl-bg-blue-100';

View File

@ -160,7 +160,12 @@ export default {
wclass="report-block-list"
class="report-block-container"
>
<li v-for="data in fullData" :key="data.id" class="gl-display-flex gl-align-items-center">
<li
v-for="data in fullData"
:key="data.id"
class="gl-display-flex gl-align-items-center"
data-testid="extension-list-item"
>
<status-icon v-if="data.icon" :icon-name="data.icon.name" :size="12" />
<div class="gl-mt-2 gl-mb-2 gl-flex-wrap gl-align-self-center gl-display-flex">
<div v-safe-html="data.text" class="gl-mr-4"></div>

View File

@ -746,33 +746,14 @@
}
}
.add-issuable-form-input-token-list {
display: flex;
flex-wrap: wrap;
align-items: baseline;
list-style: none;
margin-bottom: 0;
padding-left: 0;
}
.add-issuable-form-input-wrapper {
&.focus {
border-color: $blue-300;
box-shadow: 0 0 4px $dropdown-input-focus-shadow;
}
.add-issuable-form-token-list-item {
max-width: 100%;
margin-bottom: $gl-vert-padding;
margin-right: 5px;
}
.add-issuable-form-input-list-item {
flex: 1;
min-width: 200px;
margin-bottom: $gl-vert-padding;
}
.add-issuable-form-input {
width: 100%;
border: 0;
&:focus {
outline: none;
.gl-show-field-errors &.form-control:not(textarea) {
height: auto;
}
}

View File

@ -18,17 +18,21 @@ class Projects::TagsController < Projects::ApplicationController
params[:sort] = params[:sort].presence || sort_value_recently_updated
@sort = params[:sort]
@tags = TagsFinder.new(@repository, params).execute
@tags = Kaminari.paginate_array(@tags).page(params[:page])
@tags, @tags_loading_error = TagsFinder.new(@repository, params).execute
@tags = Kaminari.paginate_array(@tags).page(params[:page])
tag_names = @tags.map(&:name)
@tags_pipelines = @project.ci_pipelines.latest_successful_for_refs(tag_names)
@releases = project.releases.where(tag: tag_names)
@tag_pipeline_statuses = Ci::CommitStatusesFinder.new(@project, @repository, current_user, @tags).execute
respond_to do |format|
format.html
format.atom { render layout: 'xml.atom' }
status = @tags_loading_error ? :service_unavailable : :ok
format.html { render status: status }
format.atom { render layout: 'xml.atom', status: status }
end
end
# rubocop: enable CodeReuse/ActiveRecord

View File

@ -284,9 +284,9 @@ class ProjectsController < Projects::ApplicationController
end
if find_tags && @repository.tag_count.nonzero?
tags = TagsFinder.new(@repository, params).execute.take(100).map(&:name)
tags, _ = TagsFinder.new(@repository, params).execute
options['Tags'] = tags
options['Tags'] = tags.take(100).map(&:name)
end
# If reference is commit id - we should add it to branch/tag selectbox

View File

@ -7,6 +7,9 @@ class TagsFinder < GitRefsFinder
def execute
tags = repository.tags_sorted_by(sort)
by_search(tags)
[by_search(tags), nil]
rescue Gitlab::Git::CommandError => e
[[], e]
end
end

View File

@ -32,7 +32,7 @@ module Ci
end
def upcoming_processables
if unordered? || Feature.disabled?(:ci_resource_group_process_modes, project, default_enabled: :yaml)
if unordered?
processables.waiting_for_resource
elsif oldest_first?
processables.waiting_for_resource_or_upcoming

View File

@ -1,14 +1,17 @@
#js-authenticate-token-2fa
%a.gl-button.btn.btn-block.btn-confirm#js-login-2fa-device{ href: '#' }= _("Sign in via 2FA code")
-# haml-lint:disable InlineJavaScript
%script#js-authenticate-token-2fa-in-progress{ type: "text/template" }
%p= _("Trying to communicate with your device. Plug it in (if you haven't already) and press the button on the device now.")
-# haml-lint:disable InlineJavaScript
%script#js-authenticate-token-2fa-error{ type: "text/template" }
%div
%p <%= error_message %> (<%= error_name %>)
%a.btn.btn-default.gl-button.btn-block#js-token-2fa-try-again= _("Try again?")
-# haml-lint:disable InlineJavaScript
%script#js-authenticate-token-2fa-authenticated{ type: "text/template" }
%div
%p= _("We heard back from your device. You have been authenticated.")

View File

@ -1,8 +1,10 @@
#js-register-token-2fa
-# haml-lint:disable InlineJavaScript
%script#js-register-2fa-message{ type: "text/template" }
%p <%= message %>
-# haml-lint:disable InlineJavaScript
%script#js-register-token-2fa-setup{ type: "text/template" }
- if current_user.two_factor_otp_enabled?
.row.gl-mb-3
@ -17,12 +19,14 @@
.col-md-8
%p= _("You need to register a two-factor authentication app before you can set up a device.")
-# haml-lint:disable InlineJavaScript
%script#js-register-token-2fa-error{ type: "text/template" }
%div
%p
%span <%= error_message %> (<%= error_name %>)
%a.btn.btn-default.gl-button#js-token-2fa-try-again= _("Try again?")
-# haml-lint:disable InlineJavaScript
%script#js-register-token-2fa-registered{ type: "text/template" }
.row.gl-mb-3
.col-md-12

View File

@ -21,6 +21,7 @@
%li
%h2.breadcrumbs-sub-title{ data: { qa_selector: 'breadcrumb_sub_title_content' } }
= link_to @breadcrumb_title, breadcrumb_title_link
-# haml-lint:disable InlineJavaScript
%script{ type: 'application/ld+json' }
:plain
#{schema_breadcrumb_json}

View File

@ -31,4 +31,5 @@
.form-actions
= button_tag 'Create branch', class: 'gl-button btn btn-confirm'
= link_to _('Cancel'), project_branches_path(@project), class: 'gl-button btn btn-default btn-cancel'
-# haml-lint:disable InlineJavaScript
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe

View File

@ -49,6 +49,7 @@
= render "projects/merge_requests/tabs/pane", id: "notes", class: "notes voting_notes" do
.row
%section.col-md-12
-# haml-lint:disable InlineJavaScript
%script.js-notes-data{ type: "application/json" }= initial_notes_data(true).to_json.html_safe
.issuable-discussion.js-vue-notes-event
- if @merge_request.description.present?

View File

@ -12,5 +12,6 @@
%code
= @event.as_json_wo_empty
-# haml-lint:disable InlineJavaScript
:javascript
#{render 'tracker'}

View File

@ -18,6 +18,9 @@
= render_if_exists 'projects/commits/mirror_status'
- if @tags_loading_error
= render 'shared/errors/gitaly_unavailable', reason: s_('TagsPage|Unable to load tags')
.tags
- if @tags.any?
%ul.flex-list.content-list

View File

@ -54,4 +54,5 @@
.form-actions.gl-display-flex
= button_tag s_('TagsPage|Create tag'), class: 'gl-button btn btn-confirm gl-mr-3', data: { qa_selector: "create_tag_button" }
= link_to s_('TagsPage|Cancel'), project_tags_path(@project), class: 'gl-button btn btn-default btn-cancel'
-# haml-lint:disable InlineJavaScript
%script#availableRefs{ type: "application/json" }= @project.repository.ref_names.to_json.html_safe

View File

@ -0,0 +1,8 @@
.gl-alert.gl-alert-danger.gl-mb-5.gl-mt-5
.gl-alert-container
= sprite_icon('error', size: 16, css_class: 'gl-icon gl-alert-icon gl-alert-icon-no-title')
.gl-alert-content
.gl-alert-title
= reason
.gl-alert-body
= s_('The git server, Gitaly, is not available at this time. Please contact your administrator.')

View File

@ -7,6 +7,7 @@
- can_edit_issuable = issuable_sidebar.dig(:current_user, :can_edit)
- add_page_startup_api_call "#{issuable_sidebar[:issuable_json_path]}?serializer=sidebar_extras"
- reviewers = local_assigns.fetch(:reviewers, nil)
- in_group_context_with_iterations = @project.group.present? && issuable_sidebar[:supports_iterations]
%aside.right-sidebar.js-right-sidebar.js-issuable-sidebar{ data: { signed: { in: signed_in }, issuable_type: issuable_type }, class: sidebar_gutter_collapsed_class, 'aria-live' => 'polite', 'aria-label': issuable_type }
.issuable-sidebar
@ -28,11 +29,11 @@
= render_if_exists 'shared/issuable/sidebar_item_epic', issuable_sidebar: issuable_sidebar, group_path: @project.group.full_path, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid], issuable_type: issuable_type
- if issuable_sidebar[:supports_milestone]
.block.milestone{ :class => ("gl-border-b-0!" if issuable_sidebar[:supports_iterations]), data: { qa_selector: 'milestone_block' } }
.block.milestone{ :class => ("gl-border-b-0!" if in_group_context_with_iterations), data: { qa_selector: 'milestone_block' } }
.js-milestone-select{ data: { can_edit: can_edit_issuable.to_s, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid] } }
- if @project.group.present? && issuable_sidebar[:supports_iterations]
.block{ class: 'gl-pt-0!', data: { qa_selector: 'iteration_container' } }
- if in_group_context_with_iterations
.block{ class: 'gl-pt-0! gl-collapse-empty', data: { qa_selector: 'iteration_container', testid: 'iteration_container' } }<
= render_if_exists 'shared/issuable/iteration_select', can_edit: can_edit_issuable.to_s, group_path: @project.group.full_path, project_path: issuable_sidebar[:project_full_path], issue_iid: issuable_sidebar[:iid], issuable_type: issuable_type
- if issuable_sidebar[:supports_time_tracking]
@ -55,11 +56,13 @@
.js-sidebar-status-entry-point{ data: sidebar_status_data(issuable_sidebar, @project) }
- if issuable_sidebar.has_key?(:confidential)
-# haml-lint:disable InlineJavaScript
%script#js-confidential-issue-data{ type: "application/json" }= { is_confidential: issuable_sidebar[:confidential], is_editable: can_edit_issuable }.to_json.html_safe
#js-confidential-entry-point
= render_if_exists 'shared/issuable/sidebar_cve_id_request', issuable_sidebar: issuable_sidebar
-# haml-lint:disable InlineJavaScript
%script#js-lock-issue-data{ type: "application/json" }= { is_locked: !!issuable_sidebar[:discussion_locked], is_editable: can_edit_issuable }.to_json.html_safe
#js-lock-entry-point

View File

@ -25,4 +25,5 @@
= sprite_icon('lock', css_class: 'icon')
%span
= html_escape(_("This %{issuable} is locked. Only %{strong_open}project members%{strong_close} can comment.")) % { issuable: issuable.class.to_s.titleize.downcase, strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe }
-# haml-lint:disable InlineJavaScript
%script.js-notes-data{ type: "application/json" }= initial_notes_data(autocomplete).to_json.html_safe

View File

@ -2,7 +2,7 @@
name: advanced_search_multi_project_select
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62606
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/333011
milestone: '14.0'
milestone: '14.4'
type: development
group: group::global search
default_enabled: false

View File

@ -1,8 +1,8 @@
---
name: ci_resource_group_process_modes
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/67015
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/340380
milestone: '14.3'
name: refactor_mr_widgets_extensions
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70993
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341759
milestone: '14.4'
type: development
group: group::release
group: group::code review
default_enabled: false

View File

@ -0,0 +1,8 @@
---
name: refactor_mr_widgets_extensions_user
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/70993
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341759
milestone: '14.4'
type: development
group: group::code review
default_enabled: false

View File

@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/341849
milestone: '14.4'
type: development
group: group::source code
default_enabled: false
default_enabled: true

View File

@ -1,6 +1,6 @@
---
stage: Release
group: Release
stage: Manage
group: Optimize
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: reference, api
---

View File

@ -1,6 +1,6 @@
---
stage: Release
group: Release
stage: Manage
group: Optimize
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
type: reference, api
---

View File

@ -66,13 +66,9 @@ Only one resource can be attached to a resource group.
## Process modes
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202186) in GitLab 14.3.
FLAG:
On self-managed GitLab, by default this feature is not available.
To make it available, ask an administrator to [enable the `ci_resource_group_process_modes` flag](../../administration/feature_flags.md).
On GitLab.com, this feature is not available.
The feature is not ready for production use.
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/202186) in GitLab 14.3.
> - [Feature flag `ci_resource_group_process_modes`](https://gitlab.com/gitlab-org/gitlab/-/issues/340380) removed in GitLab 14.4.
> - [Generally available](https://gitlab.com/gitlab-org/gitlab/-/issues/202186) in GitLab 14.4.
You can choose a process mode to strategically control the job concurrency for your deployment preferences.
The following modes are supported:

View File

@ -15,7 +15,6 @@ GitLab uses [Redis](https://redis.io) for the following distinct purposes:
- To manage the shared application state.
- To store CI trace chunks.
- As a Pub/Sub queue backend for ActionCable.
- CI trace chunk storage.
- Rate limiting state storage.
In most environments (including the GDK), all of these point to the same

View File

@ -1,9 +1,9 @@
# frozen_string_literal: true
unless Rails.env.production?
require_dependency 'haml_lint/haml_visitor'
require_dependency 'haml_lint/linter'
require_dependency 'haml_lint/linter_registry'
require 'haml_lint/haml_visitor'
require 'haml_lint/linter'
require 'haml_lint/linter_registry'
module HamlLint
class Linter::InlineJavaScript < Linter

View File

@ -32,7 +32,6 @@ module API
values: ::Ci::ResourceGroup.process_modes.keys
end
put ':id/resource_groups/:key' do
not_found! unless ::Feature.enabled?(:ci_resource_group_process_modes, user_project, default_enabled: :yaml)
authorize! :update_resource_group, resource_group
if resource_group.update(declared_params(include_missing: false))

View File

@ -24,7 +24,7 @@ module API
use :pagination
end
get ':id/repository/tags', feature_category: :source_code_management do
tags = ::TagsFinder.new(user_project.repository,
tags, _ = ::TagsFinder.new(user_project.repository,
sort: "#{params[:order_by]}_#{params[:sort]}",
search: params[:search]).execute

View File

@ -6252,9 +6252,6 @@ msgstr ""
msgid "Cannot assign a confidential epic to a non-confidential issue. Make the issue confidential and try again"
msgstr ""
msgid "Cannot be assigned to other projects."
msgstr ""
msgid "Cannot be merged automatically"
msgstr ""
@ -23169,9 +23166,6 @@ msgstr ""
msgid "Not available for protected branches"
msgstr ""
msgid "Not available to run jobs."
msgstr ""
msgid "Not confidential"
msgstr ""
@ -29214,6 +29208,15 @@ msgstr ""
msgid "Runners|Are you sure you want to delete this runner?"
msgstr ""
msgid "Runners|Associated with one or more projects"
msgstr ""
msgid "Runners|Available to all projects"
msgstr ""
msgid "Runners|Available to all projects and subgroups in the group"
msgstr ""
msgid "Runners|Can run untagged jobs"
msgstr ""
@ -29274,6 +29277,9 @@ msgstr ""
msgid "Runners|New runner, has not connected yet"
msgstr ""
msgid "Runners|Not available to run jobs"
msgstr ""
msgid "Runners|Not connected"
msgstr ""
@ -29397,6 +29403,9 @@ msgstr ""
msgid "Runners|You can set up a specific runner to be used by multiple projects but you cannot make this a shared runner."
msgstr ""
msgid "Runners|You cannot assign to other projects"
msgstr ""
msgid "Runners|You have used %{quotaUsed} out of %{quotaLimit} of your shared Runners pipeline minutes."
msgstr ""
@ -29427,15 +29436,6 @@ msgstr ""
msgid "Runs a number of housekeeping tasks within the current repository, such as compressing file revisions and removing unreachable objects."
msgstr ""
msgid "Runs jobs from all unassigned projects in its group."
msgstr ""
msgid "Runs jobs from all unassigned projects."
msgstr ""
msgid "Runs jobs from assigned projects."
msgstr ""
msgid "SAML"
msgstr ""
@ -33197,6 +33197,9 @@ msgstr ""
msgid "TagsPage|This tag has no release notes."
msgstr ""
msgid "TagsPage|Unable to load tags"
msgstr ""
msgid "TagsPage|Use git tag command to add a new one:"
msgstr ""
@ -33779,6 +33782,9 @@ msgstr ""
msgid "The form contains the following warning:"
msgstr ""
msgid "The git server, Gitaly, is not available at this time. Please contact your administrator."
msgstr ""
msgid "The global settings require you to enable Two-Factor Authentication for your account."
msgstr ""
@ -39775,6 +39781,11 @@ msgstr ""
msgid "ciReport|Investigate this vulnerability by creating an issue"
msgstr ""
msgid "ciReport|Load performance test metrics detected %{strongStart}%{changesFound}%{strongEnd} change"
msgid_plural "ciReport|Load performance test metrics detected %{strongStart}%{changesFound}%{strongEnd} changes"
msgstr[0] ""
msgstr[1] ""
msgid "ciReport|Load performance test metrics: "
msgstr ""

View File

@ -55,9 +55,9 @@
"@babel/preset-env": "^7.10.1",
"@gitlab/at.js": "1.5.7",
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/svgs": "1.213.0",
"@gitlab/svgs": "1.215.0",
"@gitlab/tributejs": "1.0.0",
"@gitlab/ui": "32.15.0",
"@gitlab/ui": "32.18.0",
"@gitlab/visual-review-tools": "1.6.1",
"@rails/actioncable": "6.1.4-1",
"@rails/ujs": "6.1.4-1",

View File

@ -14,6 +14,22 @@ module QA
:name,
:full_path
# Get group projects
#
# @return [Array<QA::Resource::Project>]
def projects
parse_body(api_get_from("#{api_get_path}/projects")).map do |project|
Project.init do |resource|
resource.api_client = api_client
resource.group = self
resource.id = project[:id]
resource.name = project[:name]
resource.description = project[:description]
resource.path_with_namespace = project[:path_with_namespace]
end
end
end
# Get group labels
#
# @return [Array<QA::Resource::GroupLabel>]

View File

@ -358,6 +358,46 @@ module QA
parse_body(response)
end
# Object comparison
#
# @param [QA::Resource::Project] other
# @return [Boolean]
def ==(other)
other.is_a?(Project) && comparable_project == other.comparable_project
end
# Override inspect for a better rspec failure diff output
#
# @return [String]
def inspect
JSON.pretty_generate(comparable_project)
end
protected
# Return subset of fields for comparing projects
#
# @return [Hash]
def comparable_project
reload! if api_response.nil?
api_resource.slice(
:name,
:path,
:description,
:tag_list,
:archived,
:issues_enabled,
:merge_request_enabled,
:wiki_enabled,
:jobs_enabled,
:snippets_enabled,
:shared_runners_enabled,
:request_access_enabled,
:avatar_url
)
end
private
def transform_api_resource(api_resource)

View File

@ -0,0 +1,76 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Manage', :requires_admin do
describe 'Bulk project import' do
let!(:staging?) { Runtime::Scenario.gitlab_address.include?('staging.gitlab.com') }
let(:import_wait_duration) { { max_duration: 300, sleep_interval: 2 } }
let(:admin_api_client) { Runtime::API::Client.as_admin }
let(:user) do
Resource::User.fabricate_via_api! do |usr|
usr.api_client = admin_api_client
usr.hard_delete_on_api_removal = true
end
end
let(:api_client) { Runtime::API::Client.new(user: user) }
let(:sandbox) do
Resource::Sandbox.fabricate_via_api! do |group|
group.api_client = admin_api_client
end
end
let(:source_group) do
Resource::Sandbox.fabricate_via_api! do |group|
group.api_client = api_client
group.path = "source-group-for-import-#{SecureRandom.hex(4)}"
end
end
let(:source_project) do
Resource::Project.fabricate_via_api! do |project|
project.api_client = api_client
project.group = source_group
end
end
let(:imported_group) do
Resource::BulkImportGroup.fabricate_via_api! do |group|
group.api_client = api_client
group.sandbox = sandbox
group.source_group_path = source_group.path
end
end
before do
Runtime::Feature.enable(:bulk_import_projects)
Runtime::Feature.enable(:top_level_group_creation_enabled) if staging?
sandbox.add_member(user, Resource::Members::AccessLevel::MAINTAINER)
source_project # fabricate source group and project
end
after do
user.remove_via_api!
ensure
Runtime::Feature.disable(:bulk_import_projects)
Runtime::Feature.disable(:top_level_group_creation_enabled) if staging?
end
context 'with project' do
it 'successfully imports project' do
expect { imported_group.import_status }.to eventually_eq('finished').within(import_wait_duration)
imported_projects = imported_group.reload!.projects
aggregate_failures do
expect(imported_projects.count).to eq(1)
expect(imported_projects.first).to eq(source_project)
end
end
end
end
end
end

View File

@ -17,6 +17,25 @@ RSpec.describe Projects::TagsController do
expect(assigns(:tags).map(&:name)).to include('v1.1.0', 'v1.0.0')
end
context 'when Gitaly is unavailable' do
where(:format) do
[:html, :atom]
end
with_them do
it 'returns 503 status code' do
expect_next_instance_of(TagsFinder) do |finder|
expect(finder).to receive(:execute).and_return([[], Gitlab::Git::CommandError.new])
end
get :index, params: { namespace_id: project.namespace.to_param, project_id: project }, format: format
expect(assigns(:tags)).to eq([])
expect(response).to have_gitlab_http_status(:service_unavailable)
end
end
end
it 'returns releases matching those tags' do
subject

View File

@ -231,8 +231,8 @@ RSpec.describe 'Related issues', :js do
it 'add related issue' do
click_button 'Add a related issue'
find('.js-add-issuable-form-input').set "#{issue_b.to_reference(project)} "
find('.js-add-issuable-form-add-button').click
fill_in 'Paste issue link', with: "#{issue_b.to_reference(project)} "
click_button 'Add'
wait_for_requests
@ -248,8 +248,8 @@ RSpec.describe 'Related issues', :js do
it 'add cross-project related issue' do
click_button 'Add a related issue'
find('.js-add-issuable-form-input').set "#{issue_project_b_a.to_reference(project)} "
find('.js-add-issuable-form-add-button').click
fill_in 'Paste issue link', with: "#{issue_project_b_a.to_reference(project)} "
click_button 'Add'
wait_for_requests
@ -262,8 +262,8 @@ RSpec.describe 'Related issues', :js do
it 'pressing enter should submit the form' do
click_button 'Add a related issue'
find('.js-add-issuable-form-input').set "#{issue_project_b_a.to_reference(project)} "
find('.js-add-issuable-form-input').native.send_key(:enter)
fill_in 'Paste issue link', with: "#{issue_project_b_a.to_reference(project)} "
find_field('Paste issue link').native.send_key(:enter)
wait_for_requests
@ -276,9 +276,9 @@ RSpec.describe 'Related issues', :js do
it 'disallows duplicate entries' do
click_button 'Add a related issue'
find('.js-add-issuable-form-input').set 'duplicate duplicate duplicate'
fill_in 'Paste issue link', with: 'duplicate duplicate duplicate'
items = all('.js-add-issuable-form-token-list-item')
items = all('.issue-token')
expect(items.count).to eq(1)
expect(items[0].text).to eq('duplicate')
@ -289,28 +289,34 @@ RSpec.describe 'Related issues', :js do
it 'allows us to remove pending issues' do
# Tests against https://gitlab.com/gitlab-org/gitlab/issues/11625
click_button 'Add a related issue'
find('.js-add-issuable-form-input').set 'issue1 issue2 issue3 '
fill_in 'Paste issue link', with: 'issue1 issue2 issue3 '
items = all('.js-add-issuable-form-token-list-item')
items = all('.issue-token')
expect(items.count).to eq(3)
expect(items[0].text).to eq('issue1')
expect(items[1].text).to eq('issue2')
expect(items[2].text).to eq('issue3')
# Remove pending issues left to right to make sure none get stuck
items[0].find('.js-issue-token-remove-button').click
items = all('.js-add-issuable-form-token-list-item')
within items[0] do
click_button 'Remove'
end
items = all('.issue-token')
expect(items.count).to eq(2)
expect(items[0].text).to eq('issue2')
expect(items[1].text).to eq('issue3')
items[0].find('.js-issue-token-remove-button').click
items = all('.js-add-issuable-form-token-list-item')
within items[0] do
click_button 'Remove'
end
items = all('.issue-token')
expect(items.count).to eq(1)
expect(items[0].text).to eq('issue3')
items[0].find('.js-issue-token-remove-button').click
items = all('.js-add-issuable-form-token-list-item')
within items[0] do
click_button 'Remove'
end
items = all('.issue-token')
expect(items.count).to eq(0)
end
end
@ -352,8 +358,8 @@ RSpec.describe 'Related issues', :js do
it 'add related issue' do
click_button 'Add a related issue'
find('.js-add-issuable-form-input').set "##{issue_d.iid} "
find('.js-add-issuable-form-add-button').click
fill_in 'Paste issue link', with: "##{issue_d.iid} "
click_button 'Add'
wait_for_requests
@ -368,8 +374,8 @@ RSpec.describe 'Related issues', :js do
it 'add invalid related issue' do
click_button 'Add a related issue'
find('.js-add-issuable-form-input').set "#9999999 "
find('.js-add-issuable-form-add-button').click
fill_in 'Paste issue link', with: '#9999999 '
click_button 'Add'
wait_for_requests
@ -383,8 +389,8 @@ RSpec.describe 'Related issues', :js do
it 'add unauthorized related issue' do
click_button 'Add a related issue'
find('.js-add-issuable-form-input').set "#{issue_project_unauthorized_a.to_reference(project)} "
find('.js-add-issuable-form-add-button').click
fill_in 'Paste issue link', with: "#{issue_project_unauthorized_a.to_reference(project)} "
click_button 'Add'
wait_for_requests

View File

@ -8,7 +8,7 @@ RSpec.describe Ci::CommitStatusesFinder, '#execute' do
let_it_be(:user) { create(:user) }
context 'tag refs' do
let_it_be(:tags) { TagsFinder.new(project.repository, {}).execute }
let_it_be(:tags) { project.repository.tags }
let(:subject) { described_class.new(project, project.repository, user, tags).execute }
@ -131,7 +131,7 @@ RSpec.describe Ci::CommitStatusesFinder, '#execute' do
end
context 'CI pipelines visible to' do
let_it_be(:tags) { TagsFinder.new(project.repository, {}).execute }
let_it_be(:tags) { project.repository.tags }
let(:subject) { described_class.new(project, project.repository, user, tags).execute }
@ -161,7 +161,7 @@ RSpec.describe Ci::CommitStatusesFinder, '#execute' do
context 'when not a member of a private project' do
let(:private_project) { create(:project, :private, :repository) }
let(:private_tags) { TagsFinder.new(private_tags.repository, {}).execute }
let(:private_tags) { private_tags.repository.tags }
let(:private_subject) { described_class.new(private_project, private_project.repository, user, tags).execute }
before do

View File

@ -3,93 +3,76 @@
require 'spec_helper'
RSpec.describe TagsFinder do
let(:user) { create(:user) }
let(:project) { create(:project, :repository) }
let(:repository) { project.repository }
let_it_be(:user) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:repository) { project.repository }
def load_tags(params)
tags_finder = described_class.new(repository, params)
tags, error = tags_finder.execute
expect(error).to eq(nil)
tags
end
describe '#execute' do
context 'sort only' do
it 'sorts by name' do
tags_finder = described_class.new(repository, {})
result = tags_finder.execute
expect(result.first.name).to eq("v1.0.0")
expect(load_tags({}).first.name).to eq("v1.0.0")
end
it 'sorts by recently_updated' do
tags_finder = described_class.new(repository, { sort: 'updated_desc' })
result = tags_finder.execute
recently_updated_tag = repository.tags.max do |a, b|
repository.commit(a.dereferenced_target).committed_date <=> repository.commit(b.dereferenced_target).committed_date
end
expect(result.first.name).to eq(recently_updated_tag.name)
params = { sort: 'updated_desc' }
expect(load_tags(params).first.name).to eq(recently_updated_tag.name)
end
it 'sorts by last_updated' do
tags_finder = described_class.new(repository, { sort: 'updated_asc' })
params = { sort: 'updated_asc' }
result = tags_finder.execute
expect(result.first.name).to eq('v1.0.0')
expect(load_tags(params).first.name).to eq('v1.0.0')
end
end
context 'filter only' do
it 'filters tags by name' do
tags_finder = described_class.new(repository, { search: '1.0.0' })
result = tags_finder.execute
result = load_tags({ search: '1.0.0' })
expect(result.first.name).to eq('v1.0.0')
expect(result.count).to eq(1)
end
it 'does not find any tags with that name' do
tags_finder = described_class.new(repository, { search: 'hey' })
result = tags_finder.execute
expect(result.count).to eq(0)
expect(load_tags({ search: 'hey' }).count).to eq(0)
end
it 'filters tags by name that begins with' do
params = { search: '^v1.0' }
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
result = load_tags({ search: '^v1.0' })
expect(result.first.name).to eq('v1.0.0')
expect(result.count).to eq(1)
end
it 'filters tags by name that ends with' do
params = { search: '0.0$' }
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
result = load_tags({ search: '0.0$' })
expect(result.first.name).to eq('v1.0.0')
expect(result.count).to eq(1)
end
it 'filters tags by nonexistent name that begins with' do
params = { search: '^nope' }
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
result = load_tags({ search: '^nope' })
expect(result.count).to eq(0)
end
it 'filters tags by nonexistent name that ends with' do
params = { search: 'nope$' }
tags_finder = described_class.new(repository, params)
result = tags_finder.execute
result = load_tags({ search: 'nope$' })
expect(result.count).to eq(0)
end
end
@ -97,7 +80,7 @@ RSpec.describe TagsFinder do
context 'filter and sort' do
let(:tags_to_compare) { %w[v1.0.0 v1.1.0] }
subject { described_class.new(repository, params).execute.select { |tag| tags_to_compare.include?(tag.name) } }
subject { load_tags(params).select { |tag| tags_to_compare.include?(tag.name) } }
context 'when sort by updated_desc' do
let(:params) { { sort: 'updated_desc', search: 'v1' } }
@ -117,5 +100,17 @@ RSpec.describe TagsFinder do
end
end
end
context 'when Gitaly is unavailable' do
it 'returns empty list of tags' do
expect(Gitlab::GitalyClient).to receive(:call).and_raise(GRPC::Unavailable)
tags_finder = described_class.new(repository, {})
tags, error = tags_finder.execute
expect(error).to be_a(Gitlab::Git::CommandError)
expect(tags).to eq([])
end
end
end
end

View File

@ -1,5 +1,6 @@
import { mount, shallowMount } from '@vue/test-utils';
import AddIssuableForm from '~/related_issues/components/add_issuable_form.vue';
import IssueToken from '~/related_issues/components/issue_token.vue';
import { issuableTypesMap, linkedIssueTypesMap, PathIdSeparator } from '~/related_issues/constants';
const issuable1 = {
@ -22,7 +23,7 @@ const issuable2 = {
const pathIdSeparator = PathIdSeparator.Issue;
const findFormInput = (wrapper) => wrapper.find('.js-add-issuable-form-input').element;
const findFormInput = (wrapper) => wrapper.find('input').element;
const findRadioInput = (inputs, value) =>
inputs.filter((input) => input.element.value === value)[0];
@ -105,11 +106,11 @@ describe('AddIssuableForm', () => {
});
it('should put input value in place', () => {
expect(findFormInput(wrapper).value).toEqual(inputValue);
expect(findFormInput(wrapper).value).toBe(inputValue);
});
it('should render pending issuables items', () => {
expect(wrapper.findAll('.js-add-issuable-form-token-list-item').length).toEqual(2);
expect(wrapper.findAllComponents(IssueToken)).toHaveLength(2);
});
it('should not have disabled submit button', () => {

View File

@ -0,0 +1,45 @@
import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import RunnerStateLockedBadge from '~/runner/components/runner_state_locked_badge.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
describe('RunnerTypeBadge', () => {
let wrapper;
const findBadge = () => wrapper.findComponent(GlBadge);
const getTooltip = () => getBinding(findBadge().element, 'gl-tooltip');
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMount(RunnerStateLockedBadge, {
propsData: {
...props,
},
directives: {
GlTooltip: createMockDirective(),
},
});
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('renders locked state', () => {
expect(wrapper.text()).toBe('locked');
expect(findBadge().props('variant')).toBe('warning');
});
it('renders tooltip', () => {
expect(getTooltip().value).toBeDefined();
});
it('passes arbitrary attributes to the badge', () => {
createComponent({ props: { size: 'sm' } });
expect(findBadge().props('size')).toBe('sm');
});
});

View File

@ -0,0 +1,45 @@
import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import RunnerStatePausedBadge from '~/runner/components/runner_state_paused_badge.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
describe('RunnerTypeBadge', () => {
let wrapper;
const findBadge = () => wrapper.findComponent(GlBadge);
const getTooltip = () => getBinding(findBadge().element, 'gl-tooltip');
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMount(RunnerStatePausedBadge, {
propsData: {
...props,
},
directives: {
GlTooltip: createMockDirective(),
},
});
};
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('renders paused state', () => {
expect(wrapper.text()).toBe('paused');
expect(findBadge().props('variant')).toBe('danger');
});
it('renders tooltip', () => {
expect(getTooltip().value).toBeDefined();
});
it('passes arbitrary attributes to the badge', () => {
createComponent({ props: { size: 'sm' } });
expect(findBadge().props('size')).toBe('sm');
});
});

View File

@ -1,18 +1,23 @@
import { GlBadge } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
describe('RunnerTypeBadge', () => {
let wrapper;
const findBadge = () => wrapper.findComponent(GlBadge);
const getTooltip = () => getBinding(findBadge().element, 'gl-tooltip');
const createComponent = ({ props = {} } = {}) => {
wrapper = shallowMount(RunnerTypeBadge, {
propsData: {
...props,
},
directives: {
GlTooltip: createMockDirective(),
},
});
};
@ -20,16 +25,24 @@ describe('RunnerTypeBadge', () => {
wrapper.destroy();
});
it.each`
describe.each`
type | text | variant
${INSTANCE_TYPE} | ${'shared'} | ${'success'}
${GROUP_TYPE} | ${'group'} | ${'success'}
${PROJECT_TYPE} | ${'specific'} | ${'info'}
`('displays $type runner with as "$text" with a $variant variant ', ({ type, text, variant }) => {
createComponent({ props: { type } });
`('displays $type runner', ({ type, text, variant }) => {
beforeEach(() => {
createComponent({ props: { type } });
});
expect(findBadge().text()).toBe(text);
expect(findBadge().props('variant')).toBe(variant);
it(`as "${text}" with a ${variant} variant`, () => {
expect(findBadge().text()).toBe(text);
expect(findBadge().props('variant')).toBe(variant);
});
it('with a tooltip', () => {
expect(getTooltip().value).toBeDefined();
});
});
it('validation fails for an incorrect type', () => {

View File

@ -12,6 +12,7 @@ RSpec.describe Gitlab::Git::WrapsGitalyErrors do
mapping = {
GRPC::NotFound => Gitlab::Git::Repository::NoRepository,
GRPC::InvalidArgument => ArgumentError,
GRPC::DeadlineExceeded => Gitlab::Git::CommandTimedOut,
GRPC::BadStatus => Gitlab::Git::CommandError
}

View File

@ -117,14 +117,6 @@ RSpec.describe Ci::ResourceGroup do
expect(subject[3]).to eq(build_2_waiting_for_resource)
expect(subject[4..5]).to contain_exactly(build_2_created, build_2_scheduled)
end
context 'when ci_resource_group_process_modes feature flag is disabled' do
it 'returns correct jobs in an indeterministic order' do
stub_feature_flags(ci_resource_group_process_modes: false)
expect(subject).to contain_exactly(build_1_waiting_for_resource, build_2_waiting_for_resource)
end
end
end
context 'when process mode is newest_first' do
@ -136,14 +128,6 @@ RSpec.describe Ci::ResourceGroup do
expect(subject[3]).to eq(build_1_waiting_for_resource)
expect(subject[4..5]).to contain_exactly(build_1_created, build_1_scheduled)
end
context 'when ci_resource_group_process_modes feature flag is disabled' do
it 'returns correct jobs in an indeterministic order' do
stub_feature_flags(ci_resource_group_process_modes: false)
expect(subject).to contain_exactly(build_1_waiting_for_resource, build_2_waiting_for_resource)
end
end
end
context 'when process mode is unknown' do

View File

@ -62,18 +62,6 @@ RSpec.describe API::Ci::ResourceGroups do
expect(json_response['process_mode']).to eq('oldest_first')
end
context 'when ci_resource_group_process_modes feature flag is disabled' do
before do
stub_feature_flags(ci_resource_group_process_modes: false)
end
it 'returns not found' do
subject
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'with invalid parameter' do
let(:params) { { process_mode: :unknown } }

View File

@ -3,10 +3,11 @@
require 'spec_helper'
RSpec.describe 'projects/tags/index.html.haml' do
let(:project) { create(:project, :repository) }
let(:tags) { TagsFinder.new(project.repository, {}).execute }
let(:git_tag) { project.repository.tags.last }
let(:release) { create(:release, project: project, sha: git_tag.target_commit.sha) }
let_it_be(:project) { create(:project, :repository) }
let_it_be(:tags) { project.repository.tags }
let_it_be(:git_tag) { project.repository.tags.last }
let_it_be(:release) { create(:release, project: project, sha: git_tag.target_commit.sha) }
let(:pipeline) { create(:ci_pipeline, :success, project: project, ref: git_tag.name, sha: release.sha) }
before do
@ -86,4 +87,17 @@ RSpec.describe 'projects/tags/index.html.haml' do
expect(page.all('.tags .content-list li')).not_to have_css 'svg.s24'
end
end
context 'when Gitaly is unavailable' do
it 'renders an error' do
assign(:tags_loading_error, GRPC::Unavailable.new)
content = render
expect(content).to include("Unable to load tags")
expect(content).to include(
"The git server, Gitaly, is not available at this time. Please contact your administrator."
)
end
end
end

View File

@ -904,20 +904,20 @@
stylelint-declaration-strict-value "1.7.7"
stylelint-scss "3.18.0"
"@gitlab/svgs@1.213.0":
version "1.213.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.213.0.tgz#fcd9794049d2b15f5796dbab2a3d501679153582"
integrity sha512-3d9EGpEkPDeW92Xx3FueFCJFZ/yL+uv5MWCUHmSt1tP9YmUhMXw/51c43c5+V17FuCyvhJS5tm3aEg3VYoWIRA==
"@gitlab/svgs@1.215.0":
version "1.215.0"
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.215.0.tgz#f2760bbb0a38b26346e1b755e63fb63eba005edd"
integrity sha512-/bc0+EOYPQlPCMbfyOkMLxDKBn+ewEBlmTRmFwf7mXvfIRszdJPY8XCx/fJIEQwDr8+k4E28ktFnLZGnaFhCnw==
"@gitlab/tributejs@1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@gitlab/tributejs/-/tributejs-1.0.0.tgz#672befa222aeffc83e7d799b0500a7a4418e59b8"
integrity sha512-nmKw1+hB6MHvlmPz63yPwVs1qQkycHwsKgxpEbzmky16Y6mL4EJMk3w1b8QlOAF/AIAzjCERPhe/R4MJiohbZw==
"@gitlab/ui@32.15.0":
version "32.15.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.15.0.tgz#292518f1c52ef22d73cfded9d6f9f6d48a47efee"
integrity sha512-n7SwTA5Je+s/66cTXBhRdlxiJILGA+mXX6aevUFXzFvTn4a4yVb1wx4mmaMA/EB3vUxb8+u/z61CYZ4pd5JIbw==
"@gitlab/ui@32.18.0":
version "32.18.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-32.18.0.tgz#cd340f050fe0183218f6233328aca2369bd6e449"
integrity sha512-bDMmsNB9VMBX2JbezyJWfk02t0aFfAT9Ez4ALTDUJLb5/Q9GKByfE5sLycms6L1aZxzP6r1jypnu5DD0eT92eg==
dependencies:
"@babel/standalone" "^7.0.0"
bootstrap-vue "2.19.0"