Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
a3ffaa242b
commit
c4a9ca5ffc
|
@ -1,6 +1,7 @@
|
|||
<script>
|
||||
import { produce } from 'immer';
|
||||
import { sortBy } from 'lodash';
|
||||
import { GlIcon } from '@gitlab/ui';
|
||||
import { sprintf } from '~/locale';
|
||||
import { createAlert } from '~/flash';
|
||||
import { convertToGraphQLId } from '~/graphql_shared/utils';
|
||||
|
@ -16,6 +17,7 @@ export default {
|
|||
i18n: timelineFormI18n,
|
||||
components: {
|
||||
TimelineEventsForm,
|
||||
GlIcon,
|
||||
},
|
||||
inject: ['fullPath', 'issuableId'],
|
||||
props: {
|
||||
|
@ -107,11 +109,22 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<timeline-events-form
|
||||
ref="eventForm"
|
||||
:is-event-processed="createTimelineEventActive"
|
||||
:has-timeline-events="hasTimelineEvents"
|
||||
@save-event="createIncidentTimelineEvent"
|
||||
@cancel="$emit('hide-new-timeline-events-form')"
|
||||
/>
|
||||
<div
|
||||
class="gl-relative gl-display-flex gl-align-items-center"
|
||||
:class="{ 'timeline-entry-vertical-line': hasTimelineEvents }"
|
||||
>
|
||||
<div
|
||||
v-if="hasTimelineEvents"
|
||||
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-align-self-start gl-bg-white gl-text-gray-200 gl-border-gray-100 gl-border-1 gl-border-solid gl-rounded-full gl-mt-2 gl-mr-3 gl-w-8 gl-h-8 gl-z-index-1"
|
||||
>
|
||||
<gl-icon name="comment" class="note-icon" />
|
||||
</div>
|
||||
<timeline-events-form
|
||||
ref="eventForm"
|
||||
:is-event-processed="createTimelineEventActive"
|
||||
:has-timeline-events="hasTimelineEvents"
|
||||
@save-event="createIncidentTimelineEvent"
|
||||
@cancel="$emit('hide-new-timeline-events-form')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<script>
|
||||
import { GlDatepicker, GlFormInput, GlFormGroup, GlButton, GlIcon } from '@gitlab/ui';
|
||||
import { GlDatepicker, GlFormInput, GlFormGroup, GlButton } from '@gitlab/ui';
|
||||
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
|
||||
import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
|
||||
import { timelineFormI18n } from './constants';
|
||||
|
@ -23,7 +23,6 @@ export default {
|
|||
GlFormInput,
|
||||
GlFormGroup,
|
||||
GlButton,
|
||||
GlIcon,
|
||||
},
|
||||
i18n: timelineFormI18n,
|
||||
directives: {
|
||||
|
@ -48,14 +47,17 @@ export default {
|
|||
placeholderDate,
|
||||
hourPickerInput: placeholderDate.getHours(),
|
||||
minutePickerInput: placeholderDate.getMinutes(),
|
||||
datepickerTextInput: null,
|
||||
datePickerInput: placeholderDate,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
occurredAt() {
|
||||
const [years, months, days] = this.datepickerTextInput.split('-');
|
||||
const year = this.datePickerInput.getFullYear();
|
||||
const month = this.datePickerInput.getMonth();
|
||||
const day = this.datePickerInput.getDate();
|
||||
|
||||
const utcDate = new Date(
|
||||
Date.UTC(years, months - 1, days, this.hourPickerInput, this.minutePickerInput),
|
||||
Date.UTC(year, month, day, this.hourPickerInput, this.minutePickerInput),
|
||||
);
|
||||
|
||||
return utcDate.toISOString();
|
||||
|
@ -63,10 +65,10 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
clear() {
|
||||
const utcShiftedDateNow = getUtcShiftedDateNow();
|
||||
this.placeholderDate = utcShiftedDateNow;
|
||||
this.hourPickerInput = utcShiftedDateNow.getHours();
|
||||
this.minutePickerInput = utcShiftedDateNow.getMinutes();
|
||||
const newPlaceholderDate = getUtcShiftedDateNow();
|
||||
this.datePickerInput = newPlaceholderDate;
|
||||
this.hourPickerInput = newPlaceholderDate.getHours();
|
||||
this.minutePickerInput = newPlaceholderDate.getMinutes();
|
||||
this.timelineText = '';
|
||||
},
|
||||
focusDate() {
|
||||
|
@ -84,114 +86,95 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="gl-relative gl-display-flex gl-align-items-center"
|
||||
:class="{ 'timeline-entry-vertical-line': hasTimelineEvents }"
|
||||
>
|
||||
<div
|
||||
v-if="hasTimelineEvents"
|
||||
class="gl-display-flex gl-align-items-center gl-justify-content-center gl-align-self-start gl-bg-white gl-text-gray-200 gl-border-gray-100 gl-border-1 gl-border-solid gl-rounded-full gl-mt-2 gl-mr-3 gl-w-8 gl-h-8 gl-z-index-1"
|
||||
>
|
||||
<gl-icon name="comment" class="note-icon" />
|
||||
</div>
|
||||
<form class="gl-flex-grow-1 gl-border-gray-50" :class="{ 'gl-border-t': hasTimelineEvents }">
|
||||
<div
|
||||
class="gl-display-flex gl-flex-direction-column gl-sm-flex-direction-row datetime-picker"
|
||||
>
|
||||
<gl-form-group :label="__('Date')" class="gl-mt-5 gl-mr-5">
|
||||
<gl-datepicker id="incident-date" #default="{ formattedDate }" v-model="placeholderDate">
|
||||
<gl-form-input
|
||||
id="incident-date"
|
||||
ref="datepicker"
|
||||
v-model="datepickerTextInput"
|
||||
data-testid="input-datepicker"
|
||||
class="gl-datepicker-input gl-pr-7!"
|
||||
:value="formattedDate"
|
||||
:placeholder="__('YYYY-MM-DD')"
|
||||
@keydown.enter="onKeydown"
|
||||
/>
|
||||
</gl-datepicker>
|
||||
</gl-form-group>
|
||||
<div class="gl-display-flex gl-mt-5">
|
||||
<gl-form-group :label="__('Time')">
|
||||
<div class="gl-display-flex">
|
||||
<label label-for="timeline-input-hours" class="sr-only"></label>
|
||||
<gl-form-input
|
||||
id="timeline-input-hours"
|
||||
v-model="hourPickerInput"
|
||||
data-testid="input-hours"
|
||||
size="xs"
|
||||
type="number"
|
||||
min="00"
|
||||
max="23"
|
||||
/>
|
||||
<label label-for="timeline-input-minutes" class="sr-only"></label>
|
||||
<gl-form-input
|
||||
id="timeline-input-minutes"
|
||||
v-model="minutePickerInput"
|
||||
class="gl-ml-3"
|
||||
data-testid="input-minutes"
|
||||
size="xs"
|
||||
type="number"
|
||||
min="00"
|
||||
max="59"
|
||||
/>
|
||||
</div>
|
||||
</gl-form-group>
|
||||
<p class="gl-ml-3 gl-align-self-end gl-line-height-32">{{ __('UTC') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="common-note-form">
|
||||
<gl-form-group class="gl-mb-3" :label="$options.i18n.areaLabel">
|
||||
<markdown-field
|
||||
:can-attach-file="false"
|
||||
:add-spacing-classes="false"
|
||||
:show-comment-tool-bar="false"
|
||||
:textarea-value="timelineText"
|
||||
:restricted-tool-bar-items="$options.restrictedToolBarItems"
|
||||
markdown-docs-path=""
|
||||
:enable-preview="false"
|
||||
class="bordered-box gl-mt-0"
|
||||
>
|
||||
<template #textarea>
|
||||
<textarea
|
||||
v-model="timelineText"
|
||||
class="note-textarea js-gfm-input js-autosize markdown-area"
|
||||
data-testid="input-note"
|
||||
dir="auto"
|
||||
data-supports-quick-actions="false"
|
||||
:aria-label="$options.i18n.description"
|
||||
:placeholder="$options.i18n.areaPlaceholder"
|
||||
>
|
||||
</textarea>
|
||||
</template>
|
||||
</markdown-field>
|
||||
</gl-form-group>
|
||||
</div>
|
||||
<gl-form-group class="gl-mb-0">
|
||||
<gl-button
|
||||
variant="confirm"
|
||||
category="primary"
|
||||
class="gl-mr-3"
|
||||
:loading="isEventProcessed"
|
||||
@click="handleSave(false)"
|
||||
>
|
||||
{{ $options.i18n.save }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
variant="confirm"
|
||||
category="secondary"
|
||||
class="gl-mr-3 gl-ml-n2"
|
||||
:loading="isEventProcessed"
|
||||
@click="handleSave(true)"
|
||||
>
|
||||
{{ $options.i18n.saveAndAdd }}
|
||||
</gl-button>
|
||||
<gl-button class="gl-ml-n2" :disabled="isEventProcessed" @click="$emit('cancel')">
|
||||
{{ $options.i18n.cancel }}
|
||||
</gl-button>
|
||||
<div class="gl-border-b gl-pt-5"></div>
|
||||
<form class="gl-flex-grow-1 gl-border-gray-50" :class="{ 'gl-border-t': hasTimelineEvents }">
|
||||
<div class="gl-display-flex gl-flex-direction-column gl-sm-flex-direction-row">
|
||||
<gl-form-group :label="__('Date')" class="gl-mt-5 gl-mr-5">
|
||||
<gl-datepicker
|
||||
id="incident-date"
|
||||
ref="datepicker"
|
||||
v-model="datePickerInput"
|
||||
data-testid="input-datepicker"
|
||||
/>
|
||||
</gl-form-group>
|
||||
</form>
|
||||
</div>
|
||||
<div class="gl-display-flex gl-mt-5">
|
||||
<gl-form-group :label="__('Time')">
|
||||
<div class="gl-display-flex">
|
||||
<label label-for="timeline-input-hours" class="sr-only"></label>
|
||||
<gl-form-input
|
||||
id="timeline-input-hours"
|
||||
v-model="hourPickerInput"
|
||||
data-testid="input-hours"
|
||||
size="xs"
|
||||
type="number"
|
||||
min="00"
|
||||
max="23"
|
||||
/>
|
||||
<label label-for="timeline-input-minutes" class="sr-only"></label>
|
||||
<gl-form-input
|
||||
id="timeline-input-minutes"
|
||||
v-model="minutePickerInput"
|
||||
class="gl-ml-3"
|
||||
data-testid="input-minutes"
|
||||
size="xs"
|
||||
type="number"
|
||||
min="00"
|
||||
max="59"
|
||||
/>
|
||||
</div>
|
||||
</gl-form-group>
|
||||
<p class="gl-ml-3 gl-align-self-end gl-line-height-32">{{ __('UTC') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="common-note-form">
|
||||
<gl-form-group class="gl-mb-3" :label="$options.i18n.areaLabel">
|
||||
<markdown-field
|
||||
:can-attach-file="false"
|
||||
:add-spacing-classes="false"
|
||||
:show-comment-tool-bar="false"
|
||||
:textarea-value="timelineText"
|
||||
:restricted-tool-bar-items="$options.restrictedToolBarItems"
|
||||
markdown-docs-path=""
|
||||
:enable-preview="false"
|
||||
class="bordered-box gl-mt-0"
|
||||
>
|
||||
<template #textarea>
|
||||
<textarea
|
||||
v-model="timelineText"
|
||||
class="note-textarea js-gfm-input js-autosize markdown-area"
|
||||
data-testid="input-note"
|
||||
dir="auto"
|
||||
data-supports-quick-actions="false"
|
||||
:aria-label="$options.i18n.description"
|
||||
:placeholder="$options.i18n.areaPlaceholder"
|
||||
>
|
||||
</textarea>
|
||||
</template>
|
||||
</markdown-field>
|
||||
</gl-form-group>
|
||||
</div>
|
||||
<gl-form-group class="gl-mb-0">
|
||||
<gl-button
|
||||
variant="confirm"
|
||||
category="primary"
|
||||
class="gl-mr-3"
|
||||
:loading="isEventProcessed"
|
||||
@click="handleSave(false)"
|
||||
>
|
||||
{{ $options.i18n.save }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
variant="confirm"
|
||||
category="secondary"
|
||||
class="gl-mr-3 gl-ml-n2"
|
||||
:loading="isEventProcessed"
|
||||
@click="handleSave(true)"
|
||||
>
|
||||
{{ $options.i18n.saveAndAdd }}
|
||||
</gl-button>
|
||||
<gl-button class="gl-ml-n2" :disabled="isEventProcessed" @click="$emit('cancel')">
|
||||
{{ $options.i18n.cancel }}
|
||||
</gl-button>
|
||||
<div class="gl-border-b gl-pt-5"></div>
|
||||
</gl-form-group>
|
||||
</form>
|
||||
</template>
|
||||
|
|
|
@ -194,7 +194,7 @@ module Ci
|
|||
after_save :stick_build_if_status_changed
|
||||
|
||||
after_create unless: :importing? do |build|
|
||||
run_after_commit { build.feature_flagged_execute_hooks }
|
||||
run_after_commit { build.execute_hooks }
|
||||
end
|
||||
|
||||
class << self
|
||||
|
@ -285,7 +285,7 @@ module Ci
|
|||
|
||||
build.run_after_commit do
|
||||
BuildQueueWorker.perform_async(id)
|
||||
build.feature_flagged_execute_hooks
|
||||
build.execute_hooks
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -313,7 +313,7 @@ module Ci
|
|||
build.run_after_commit do
|
||||
build.ensure_persistent_ref
|
||||
|
||||
build.feature_flagged_execute_hooks
|
||||
build.execute_hooks
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -780,14 +780,6 @@ module Ci
|
|||
pending? && !any_runners_online?
|
||||
end
|
||||
|
||||
def feature_flagged_execute_hooks
|
||||
if Feature.enabled?(:execute_build_hooks_inline, project)
|
||||
execute_hooks
|
||||
else
|
||||
BuildHooksWorker.perform_async(self)
|
||||
end
|
||||
end
|
||||
|
||||
def execute_hooks
|
||||
return unless project
|
||||
return if user&.blocked?
|
||||
|
|
|
@ -7,6 +7,8 @@ module Members
|
|||
raise Gitlab::Access::AccessDeniedError unless can?(current_user, action_member_permission(permission, member), member)
|
||||
raise Gitlab::Access::AccessDeniedError if prevent_upgrade_to_owner?(member) || prevent_downgrade_from_owner?(member)
|
||||
|
||||
return success(member: member) if update_results_in_no_change?(member)
|
||||
|
||||
old_access_level = member.human_access
|
||||
old_expiry = member.expires_at
|
||||
|
||||
|
@ -26,6 +28,13 @@ module Members
|
|||
|
||||
private
|
||||
|
||||
def update_results_in_no_change?(member)
|
||||
return false if params[:expires_at]&.to_date != member.expires_at
|
||||
return false if params[:access_level] != member.access_level
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def downgrading_to_guest?
|
||||
params[:access_level] == Gitlab::Access::GUEST
|
||||
end
|
||||
|
|
|
@ -36,7 +36,7 @@ module Ci
|
|||
build.update_coverage
|
||||
Ci::BuildReportResultService.new.execute(build)
|
||||
|
||||
build.feature_flagged_execute_hooks
|
||||
build.execute_hooks
|
||||
ChatNotificationWorker.perform_async(build.id) if build.pipeline.chat?
|
||||
build.track_deployment_usage
|
||||
build.track_verify_usage
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
---
|
||||
name: execute_build_hooks_inline
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/93665
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/370387
|
||||
milestone: '15.3'
|
||||
type: development
|
||||
group: group::integrations
|
||||
default_enabled: false
|
|
@ -653,6 +653,10 @@ is the manually updated canonical Markdown+HTML examples for GLFM extensions.
|
|||
|
||||
- It contains examples in the [standard backtick-delimited `spec.txt` format](#various-markdown-specifications),
|
||||
each of which contain a Markdown example and the corresponding canonical HTML.
|
||||
- For all GitLab examples, the "extension" annotation after the backticks should consist of only
|
||||
`example gitlab`. It does not currently include any additional extension annotations describing
|
||||
the specific Markdown, unlike the GitHub Flavored Markdown examples, which do include
|
||||
these additional annotations (such as `example strikethrough`).
|
||||
- The `update-specification.rb` script inserts it as new sections before the appendix
|
||||
of generated `spec.txt`.
|
||||
- It should consist of `H1` header sections, with all examples nested either 2 or 3 levels deep
|
||||
|
@ -672,7 +676,7 @@ The actual file should not have these prefixed `|` characters.
|
|||
|
|
||||
|## Strong but with two asterisks
|
||||
|
|
||||
|```````````````````````````````` example
|
||||
|```````````````````````````````` example gitlab
|
||||
|**bold**
|
||||
|.
|
||||
|<p><strong>bold</strong></p>
|
||||
|
@ -682,7 +686,7 @@ The actual file should not have these prefixed `|` characters.
|
|||
|
|
||||
|## Strong but with HTML
|
||||
|
|
||||
|```````````````````````````````` example
|
||||
|```````````````````````````````` example gitlab
|
||||
|<strong>
|
||||
|bold
|
||||
|</strong>
|
||||
|
|
|
@ -13,7 +13,7 @@ examples may be split into multiple top-level headings in the future.
|
|||
See
|
||||
[the footnotes section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#footnotes).
|
||||
|
||||
```````````````````````````````` example gitlab footnote
|
||||
```````````````````````````````` example gitlab
|
||||
footnote reference tag [^fortytwo]
|
||||
|
||||
[^fortytwo]: footnote text
|
||||
|
@ -55,7 +55,7 @@ The following are some basic examples; more examples may be added in the future.
|
|||
|
||||
Incomplete task:
|
||||
|
||||
```````````````````````````````` example gitlab tasklist
|
||||
```````````````````````````````` example gitlab
|
||||
- [ ] incomplete
|
||||
.
|
||||
<ul>
|
||||
|
@ -69,7 +69,7 @@ incomplete
|
|||
|
||||
Completed task:
|
||||
|
||||
```````````````````````````````` example gitlab tasklist
|
||||
```````````````````````````````` example gitlab
|
||||
- [x] completed
|
||||
.
|
||||
<ul>
|
||||
|
@ -83,7 +83,7 @@ completed
|
|||
|
||||
Inapplicable task:
|
||||
|
||||
```````````````````````````````` example gitlab tasklist
|
||||
```````````````````````````````` example gitlab
|
||||
- [~] inapplicable
|
||||
.
|
||||
<ul>
|
||||
|
@ -100,7 +100,7 @@ inapplicable
|
|||
Inapplicable task in a "loose" list. Note that the `<del>` tag is not applied to the
|
||||
loose text; it has strikethrough applied with CSS.
|
||||
|
||||
```````````````````````````````` example gitlab tasklist
|
||||
```````````````````````````````` example gitlab
|
||||
- [~] inapplicable
|
||||
|
||||
text in loose list
|
||||
|
@ -131,7 +131,7 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe
|
|||
|
||||
YAML front matter:
|
||||
|
||||
```````````````````````````````` example gitlab frontmatter
|
||||
```````````````````````````````` example gitlab
|
||||
---
|
||||
title: YAML front matter
|
||||
---
|
||||
|
@ -145,7 +145,7 @@ title: YAML front matter
|
|||
|
||||
TOML front matter:
|
||||
|
||||
```````````````````````````````` example gitlab frontmatter
|
||||
```````````````````````````````` example gitlab
|
||||
+++
|
||||
title: TOML front matter
|
||||
+++
|
||||
|
@ -159,7 +159,7 @@ title: TOML front matter
|
|||
|
||||
JSON front matter:
|
||||
|
||||
```````````````````````````````` example gitlab frontmatter
|
||||
```````````````````````````````` example gitlab
|
||||
;;;
|
||||
{
|
||||
"title": "JSON front matter"
|
||||
|
@ -177,7 +177,7 @@ JSON front matter:
|
|||
|
||||
Front matter blocks should be inserted at the top of the document:
|
||||
|
||||
```````````````````````````````` example gitlab frontmatter
|
||||
```````````````````````````````` example gitlab
|
||||
text
|
||||
|
||||
---
|
||||
|
@ -191,7 +191,7 @@ title: YAML front matter
|
|||
|
||||
Front matter block delimiters shouldn’t be preceded by space characters:
|
||||
|
||||
```````````````````````````````` example gitlab frontmatter
|
||||
```````````````````````````````` example gitlab
|
||||
---
|
||||
title: YAML front matter
|
||||
---
|
||||
|
|
|
@ -9615,7 +9615,7 @@ examples may be split into multiple top-level headings in the future.
|
|||
See
|
||||
[the footnotes section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#footnotes).
|
||||
|
||||
```````````````````````````````` example gitlab footnote
|
||||
```````````````````````````````` example gitlab
|
||||
footnote reference tag [^fortytwo]
|
||||
|
||||
[^fortytwo]: footnote text
|
||||
|
@ -9657,7 +9657,7 @@ The following are some basic examples; more examples may be added in the future.
|
|||
|
||||
Incomplete task:
|
||||
|
||||
```````````````````````````````` example gitlab tasklist
|
||||
```````````````````````````````` example gitlab
|
||||
- [ ] incomplete
|
||||
.
|
||||
<ul>
|
||||
|
@ -9671,7 +9671,7 @@ incomplete
|
|||
|
||||
Completed task:
|
||||
|
||||
```````````````````````````````` example gitlab tasklist
|
||||
```````````````````````````````` example gitlab
|
||||
- [x] completed
|
||||
.
|
||||
<ul>
|
||||
|
@ -9685,7 +9685,7 @@ completed
|
|||
|
||||
Inapplicable task:
|
||||
|
||||
```````````````````````````````` example gitlab tasklist
|
||||
```````````````````````````````` example gitlab
|
||||
- [~] inapplicable
|
||||
.
|
||||
<ul>
|
||||
|
@ -9702,7 +9702,7 @@ inapplicable
|
|||
Inapplicable task in a "loose" list. Note that the `<del>` tag is not applied to the
|
||||
loose text; it has strikethrough applied with CSS.
|
||||
|
||||
```````````````````````````````` example gitlab tasklist
|
||||
```````````````````````````````` example gitlab
|
||||
- [~] inapplicable
|
||||
|
||||
text in loose list
|
||||
|
@ -9733,7 +9733,7 @@ This data can be used by static site generators like Jekyll, Hugo, and many othe
|
|||
|
||||
YAML front matter:
|
||||
|
||||
```````````````````````````````` example gitlab frontmatter
|
||||
```````````````````````````````` example gitlab
|
||||
---
|
||||
title: YAML front matter
|
||||
---
|
||||
|
@ -9747,7 +9747,7 @@ title: YAML front matter
|
|||
|
||||
TOML front matter:
|
||||
|
||||
```````````````````````````````` example gitlab frontmatter
|
||||
```````````````````````````````` example gitlab
|
||||
+++
|
||||
title: TOML front matter
|
||||
+++
|
||||
|
@ -9761,7 +9761,7 @@ title: TOML front matter
|
|||
|
||||
JSON front matter:
|
||||
|
||||
```````````````````````````````` example gitlab frontmatter
|
||||
```````````````````````````````` example gitlab
|
||||
;;;
|
||||
{
|
||||
"title": "JSON front matter"
|
||||
|
@ -9779,7 +9779,7 @@ JSON front matter:
|
|||
|
||||
Front matter blocks should be inserted at the top of the document:
|
||||
|
||||
```````````````````````````````` example gitlab frontmatter
|
||||
```````````````````````````````` example gitlab
|
||||
text
|
||||
|
||||
---
|
||||
|
@ -9793,7 +9793,7 @@ title: YAML front matter
|
|||
|
||||
Front matter block delimiters shouldn’t be preceded by space characters:
|
||||
|
||||
```````````````````````````````` example gitlab frontmatter
|
||||
```````````````````````````````` example gitlab
|
||||
---
|
||||
title: YAML front matter
|
||||
---
|
||||
|
|
|
@ -8,30 +8,50 @@ module QA
|
|||
attr_writer :name
|
||||
|
||||
attribute :id
|
||||
attribute :user_id
|
||||
attribute :expires_at
|
||||
attribute :token
|
||||
|
||||
attribute :group do
|
||||
Group.fabricate!
|
||||
end
|
||||
|
||||
attribute :token do
|
||||
Page::Group::Settings::AccessTokens.perform(&:created_access_token)
|
||||
def api_get_path
|
||||
"/groups/#{group.id}/access_tokens/#{id}"
|
||||
rescue NoValueError
|
||||
token = parse_body(api_get_from("/groups/#{group.id}/access_tokens")).find { |t| t[:name] == name }
|
||||
|
||||
raise ResourceNotFoundError unless token
|
||||
|
||||
@id = token[:id]
|
||||
retry
|
||||
end
|
||||
|
||||
def api_get_path
|
||||
"/groups/#{group.id}/access_tokens"
|
||||
def identifier
|
||||
"with name '#{name}', token's bot username '#{token_user[:username]}'"
|
||||
end
|
||||
|
||||
def api_post_path
|
||||
api_get_path
|
||||
"/groups/#{group.id}/access_tokens"
|
||||
end
|
||||
|
||||
def api_user_path
|
||||
"/users/#{user_id}"
|
||||
end
|
||||
|
||||
def token_user
|
||||
parse_body(api_get_from(api_user_path))
|
||||
end
|
||||
|
||||
def name
|
||||
@name || 'api-group-access-token'
|
||||
@name ||= "api-group-access-token-#{Faker::Alphanumeric.alphanumeric(number: 8)}"
|
||||
end
|
||||
|
||||
def api_post_body
|
||||
{
|
||||
name: name,
|
||||
scopes: ["api"]
|
||||
scopes: ["api"],
|
||||
expires_at: expires_at.to_s
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -51,6 +71,11 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
# Expire in 2 days just in case the token is created just before midnight
|
||||
def expires_at
|
||||
@expires_at || Time.now.utc.to_date + 2
|
||||
end
|
||||
|
||||
def fabricate!
|
||||
Flow::Login.sign_in_unless_signed_in
|
||||
|
||||
|
@ -59,12 +84,14 @@ module QA
|
|||
Page::Group::Menu.perform(&:go_to_access_token_settings)
|
||||
|
||||
Page::Group::Settings::AccessTokens.perform do |token_page|
|
||||
token_page.fill_token_name(name || 'api-project-access-token')
|
||||
token_page.fill_token_name(name)
|
||||
token_page.check_api
|
||||
# Expire in 2 days just in case the token is created just before midnight
|
||||
token_page.fill_expiry_date(Time.now.utc.to_date + 2)
|
||||
token_page.fill_expiry_date(expires_at)
|
||||
token_page.click_create_token_button
|
||||
self.token = token_page.created_access_token
|
||||
end
|
||||
|
||||
reload!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@ module QA
|
|||
end
|
||||
|
||||
def api_post_path
|
||||
api_get_path
|
||||
"/users/#{user.id}/impersonation_tokens"
|
||||
end
|
||||
|
||||
def name
|
||||
|
|
|
@ -8,29 +8,50 @@ module QA
|
|||
attr_writer :name
|
||||
|
||||
attribute :id
|
||||
attribute :user_id
|
||||
attribute :expires_at
|
||||
attribute :token
|
||||
|
||||
attribute :project do
|
||||
Project.fabricate!
|
||||
end
|
||||
attribute :token do
|
||||
Page::Project::Settings::AccessTokens.perform(&:created_access_token)
|
||||
end
|
||||
|
||||
def api_get_path
|
||||
"/projects/#{project.api_resource[:id]}/access_tokens"
|
||||
"/projects/#{project.id}/access_tokens/#{id}"
|
||||
rescue NoValueError
|
||||
token = parse_body(api_get_from("/projects/#{project.id}/access_tokens")).find { |t| t[:name] == name }
|
||||
|
||||
raise ResourceNotFoundError unless token
|
||||
|
||||
@id = token[:id]
|
||||
retry
|
||||
end
|
||||
|
||||
def identifier
|
||||
"with name '#{name}', token's bot username '#{token_user[:username]}'"
|
||||
end
|
||||
|
||||
def api_post_path
|
||||
api_get_path
|
||||
"/projects/#{project.id}/access_tokens"
|
||||
end
|
||||
|
||||
def api_user_path
|
||||
"/users/#{user_id}"
|
||||
end
|
||||
|
||||
def token_user
|
||||
parse_body(api_get_from(api_user_path))
|
||||
end
|
||||
|
||||
def name
|
||||
@name || 'api-project-access-token'
|
||||
@name ||= "api-project-access-token-#{Faker::Alphanumeric.alphanumeric(number: 8)}"
|
||||
end
|
||||
|
||||
def api_post_body
|
||||
{
|
||||
name: name,
|
||||
scopes: ["api"]
|
||||
scopes: ["api"],
|
||||
expires_at: expires_at.to_s
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -50,6 +71,11 @@ module QA
|
|||
end
|
||||
end
|
||||
|
||||
# Expire in 2 days just in case the token is created just before midnight
|
||||
def expires_at
|
||||
@expires_at || Time.now.utc.to_date + 2
|
||||
end
|
||||
|
||||
def fabricate!
|
||||
Flow::Login.sign_in_unless_signed_in
|
||||
|
||||
|
@ -58,12 +84,14 @@ module QA
|
|||
Page::Project::Menu.perform(&:go_to_access_token_settings)
|
||||
|
||||
Page::Project::Settings::AccessTokens.perform do |token_page|
|
||||
token_page.fill_token_name(name || 'api-project-access-token')
|
||||
token_page.fill_token_name(name)
|
||||
token_page.check_api
|
||||
# Expire in 2 days just in case the token is created just before midnight
|
||||
token_page.fill_expiry_date(Time.now.utc.to_date + 2)
|
||||
token_page.fill_expiry_date(expires_at)
|
||||
token_page.click_create_token_button
|
||||
self.token = token_page.created_access_token
|
||||
end
|
||||
|
||||
reload!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -191,9 +191,7 @@ module Glfm
|
|||
# the scripts to read them, because the scripts are run in
|
||||
# separate subprocesses, and during unit testing we are unable to substitute the mock
|
||||
# StringIO when reading the input files in the subprocess.
|
||||
[
|
||||
[ES_MARKDOWN_YML_PATH, MARKDOWN_TEMPFILE_BASENAME]
|
||||
].map do |original_file_path, tempfile_basename|
|
||||
{ ES_MARKDOWN_YML_PATH => MARKDOWN_TEMPFILE_BASENAME }.map do |original_file_path, tempfile_basename|
|
||||
Dir::Tmpname.create(tempfile_basename) do |path|
|
||||
io = File.open(original_file_path)
|
||||
io.seek(0) # rewind the file. This is necessary when testing with a mock StringIO
|
||||
|
|
|
@ -13,6 +13,8 @@ jest.mock('~/flash');
|
|||
|
||||
const fakeDate = '2020-07-08T00:00:00.000Z';
|
||||
|
||||
const mockInputDate = new Date('2021-08-12');
|
||||
|
||||
describe('Timeline events form', () => {
|
||||
// July 8 2020
|
||||
useFakeDate(fakeDate);
|
||||
|
@ -36,11 +38,10 @@ describe('Timeline events form', () => {
|
|||
const findSubmitAndAddButton = () => wrapper.findByText('Save and add another event');
|
||||
const findCancelButton = () => wrapper.findByText('Cancel');
|
||||
const findDatePicker = () => wrapper.findComponent(GlDatepicker);
|
||||
const findDatePickerInput = () => wrapper.findByTestId('input-datepicker');
|
||||
const findHourInput = () => wrapper.findByTestId('input-hours');
|
||||
const findMinuteInput = () => wrapper.findByTestId('input-minutes');
|
||||
const setDatetime = () => {
|
||||
findDatePicker().vm.$emit('input', new Date('2021-08-12'));
|
||||
findDatePicker().vm.$emit('input', mockInputDate);
|
||||
findHourInput().vm.$emit('input', 5);
|
||||
findMinuteInput().vm.$emit('input', 45);
|
||||
};
|
||||
|
@ -87,14 +88,14 @@ describe('Timeline events form', () => {
|
|||
setDatetime();
|
||||
await nextTick();
|
||||
|
||||
expect(findDatePickerInput().element.value).toBe('2021-08-12');
|
||||
expect(findDatePicker().props('value')).toBe(mockInputDate);
|
||||
expect(findHourInput().element.value).toBe('5');
|
||||
expect(findMinuteInput().element.value).toBe('45');
|
||||
|
||||
wrapper.vm.clear();
|
||||
await nextTick();
|
||||
|
||||
expect(findDatePickerInput().element.value).toBe('2020-07-08');
|
||||
expect(findDatePicker().props('value')).toStrictEqual(new Date(fakeDate));
|
||||
expect(findHourInput().element.value).toBe('0');
|
||||
expect(findMinuteInput().element.value).toBe('0');
|
||||
});
|
||||
|
|
|
@ -67,31 +67,6 @@ RSpec.describe Ci::Build do
|
|||
|
||||
create(:ci_build)
|
||||
end
|
||||
|
||||
context 'when the execute_build_hooks_inline flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(execute_build_hooks_inline: false)
|
||||
end
|
||||
|
||||
it 'uses the old job hooks worker' do
|
||||
expect(::BuildHooksWorker).to receive(:perform_async).with(Ci::Build)
|
||||
|
||||
create(:ci_build)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the execute_build_hooks_inline flag is enabled for a project' do
|
||||
before do
|
||||
stub_feature_flags(execute_build_hooks_inline: project)
|
||||
end
|
||||
|
||||
it 'executes hooks inline' do
|
||||
expect(::BuildHooksWorker).not_to receive(:perform_async)
|
||||
expect_next(described_class).to receive(:execute_hooks)
|
||||
|
||||
create(:ci_build, project: project)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -3921,18 +3896,6 @@ RSpec.describe Ci::Build do
|
|||
build.enqueue
|
||||
end
|
||||
|
||||
context 'when the execute_build_hooks_inline flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(execute_build_hooks_inline: false)
|
||||
end
|
||||
|
||||
it 'queues BuildHooksWorker' do
|
||||
expect(BuildHooksWorker).to receive(:perform_async).with(build)
|
||||
|
||||
build.enqueue
|
||||
end
|
||||
end
|
||||
|
||||
it 'executes hooks' do
|
||||
expect(build).to receive(:execute_hooks)
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@ RSpec.describe Glfm::ParseExamples, '#parse_examples' do
|
|||
|
||||
## 2.1.0 H2
|
||||
|
||||
```````````````````````````````` example gitlab extension_2.1.0-1
|
||||
```````````````````````````````` example gitlab
|
||||
example 6 md
|
||||
.
|
||||
html
|
||||
|
@ -220,7 +220,7 @@ RSpec.describe Glfm::ParseExamples, '#parse_examples' do
|
|||
disabled: false,
|
||||
end_line: 69,
|
||||
example: 6,
|
||||
extensions: %w[gitlab extension_2.1.0-1],
|
||||
extensions: %w[gitlab],
|
||||
headers: [
|
||||
'2.0.0 New H1',
|
||||
'2.1.0 H2'
|
||||
|
|
|
@ -128,7 +128,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
|
||||
## Strong but with two asterisks
|
||||
|
||||
```````````````````````````````` example gitlab strong
|
||||
```````````````````````````````` example gitlab
|
||||
**bold**
|
||||
.
|
||||
<p><strong>bold</strong></p>
|
||||
|
@ -155,7 +155,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
`source_specification` will be `gitlab`.
|
||||
|
||||
|
||||
```````````````````````````````` example gitlab strong
|
||||
```````````````````````````````` example gitlab
|
||||
<strong>
|
||||
bold
|
||||
</strong>
|
||||
|
@ -169,7 +169,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
|
||||
## Strong but skipped
|
||||
|
||||
```````````````````````````````` example gitlab strong
|
||||
```````````````````````````````` example gitlab
|
||||
**this example will be skipped**
|
||||
.
|
||||
<p><strong>this example will be skipped</strong></p>
|
||||
|
@ -177,7 +177,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
|
||||
## Strong but manually modified and skipped
|
||||
|
||||
```````````````````````````````` example gitlab strong
|
||||
```````````````````````````````` example gitlab
|
||||
**This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved**
|
||||
.
|
||||
<p><strong>This example will have its manually modified static HTML, WYSIWYG HTML, and ProseMirror JSON preserved</strong></p>
|
||||
|
@ -215,11 +215,11 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
---
|
||||
00_00_00__obsolete_entry_to_be_deleted__001:
|
||||
canonical: |
|
||||
This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted.
|
||||
This entry is no longer exists in the spec.txt, so it will be deleted.
|
||||
static: |-
|
||||
This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted.
|
||||
This entry is no longer exists in the spec.txt, so it will be deleted.
|
||||
wysiwyg: |-
|
||||
This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted.
|
||||
This entry is no longer exists in the spec.txt, so it will be deleted.
|
||||
02_01_00__inlines__strong__001:
|
||||
canonical: |
|
||||
This entry is existing, but not skipped, so it will be overwritten.
|
||||
|
@ -241,7 +241,7 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
# language=YAML
|
||||
<<~ES_PROSEMIRROR_JSON_YML_IO_EXISTING_CONTENTS
|
||||
---
|
||||
00_00_00__obsolete_entry_to_be_deleted__001:
|
||||
00_00_00__obsolete_entry_to_be_deleted__001: |-
|
||||
{
|
||||
"obsolete": "This entry is no longer exists in the spec.txt, and is not skipped, so it will be deleted."
|
||||
}
|
||||
|
@ -249,7 +249,6 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
{
|
||||
"existing": "This entry is existing, but not skipped, so it will be overwritten."
|
||||
}
|
||||
# 02_01__inlines__strong__002: is omitted from the existing file and skipped, to test that scenario.
|
||||
02_03_00__inlines__strikethrough_extension__001: |-
|
||||
{
|
||||
"type": "doc",
|
||||
|
@ -312,40 +311,42 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
allow(subject).to receive(:output)
|
||||
end
|
||||
|
||||
describe 'when skip_update_example_snapshots is truthy' do
|
||||
let(:es_examples_index_yml_contents) { reread_io(es_examples_index_yml_io) }
|
||||
let(:es_markdown_yml_contents) { reread_io(es_markdown_yml_io) }
|
||||
let(:expected_unskipped_example) do
|
||||
/05_01_00__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001/
|
||||
end
|
||||
|
||||
it 'still writes the example to examples_index.yml' do
|
||||
subject.process(skip_static_and_wysiwyg: true)
|
||||
|
||||
expect(es_examples_index_yml_contents).to match(expected_unskipped_example)
|
||||
end
|
||||
|
||||
it 'still writes the example to markdown.yml' do
|
||||
subject.process(skip_static_and_wysiwyg: true)
|
||||
|
||||
expect(es_markdown_yml_contents).to match(expected_unskipped_example)
|
||||
end
|
||||
|
||||
describe 'when any other skip_update_example_* is also truthy' do
|
||||
let(:glfm_example_status_yml_contents) do
|
||||
# language=YAML
|
||||
<<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
|
||||
---
|
||||
02_01_00__inlines__strong__001:
|
||||
skip_update_example_snapshots: 'if the skip_update_example_snapshots key is truthy...'
|
||||
skip_update_example_snapshot_html_static: '...then no other skip_update_example_* keys can be truthy'
|
||||
GLFM_EXAMPLE_STATUS_YML_CONTENTS
|
||||
describe 'glfm_example_status.yml' do
|
||||
describe 'when skip_update_example_snapshots entry is truthy' do
|
||||
let(:es_examples_index_yml_contents) { reread_io(es_examples_index_yml_io) }
|
||||
let(:es_markdown_yml_contents) { reread_io(es_markdown_yml_io) }
|
||||
let(:expected_unskipped_example) do
|
||||
/05_01_00__third_gitlab_specific_section_with_skipped_examples__strong_but_skipped__001/
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expected_msg = "Error: '02_01_00__inlines__strong__001' must not have any 'skip_update_example_snapshot_*' " \
|
||||
it 'still writes the example to examples_index.yml' do
|
||||
subject.process(skip_static_and_wysiwyg: true)
|
||||
|
||||
expect(es_examples_index_yml_contents).to match(expected_unskipped_example)
|
||||
end
|
||||
|
||||
it 'still writes the example to markdown.yml' do
|
||||
subject.process(skip_static_and_wysiwyg: true)
|
||||
|
||||
expect(es_markdown_yml_contents).to match(expected_unskipped_example)
|
||||
end
|
||||
|
||||
describe 'when any other skip_update_example_snapshot_* is also truthy' do
|
||||
let(:glfm_example_status_yml_contents) do
|
||||
# language=YAML
|
||||
<<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
|
||||
---
|
||||
02_01_00__inlines__strong__001:
|
||||
skip_update_example_snapshots: 'if the skip_update_example_snapshots key is truthy...'
|
||||
skip_update_example_snapshot_html_static: '...then no other skip_update_example_* keys can be truthy'
|
||||
GLFM_EXAMPLE_STATUS_YML_CONTENTS
|
||||
end
|
||||
|
||||
it 'raises an error' do
|
||||
expected_msg = "Error: '02_01_00__inlines__strong__001' must not have any 'skip_update_example_snapshot_*' " \
|
||||
"values specified if 'skip_update_example_snapshots' is truthy"
|
||||
expect { subject.process }.to raise_error(/#{Regexp.escape(expected_msg)}/)
|
||||
expect { subject.process }.to raise_error(/#{Regexp.escape(expected_msg)}/)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -435,6 +436,11 @@ RSpec.describe Glfm::UpdateExampleSnapshots, '#process' do
|
|||
<<~GLFM_EXAMPLE_STATUS_YML_CONTENTS
|
||||
---
|
||||
02_01_00__inlines__strong__002:
|
||||
# NOTE: 02_01_00__inlines__strong__002: is omitted from the existing prosemirror_json.yml file, and is also
|
||||
# skipped here, to show that an example does not need to exist in order to be skipped.
|
||||
# TODO: This should be changed to raise an error instead, to enforce that there cannot be orphaned
|
||||
# entries in glfm_example_status.yml. This task is captured in
|
||||
# https://gitlab.com/gitlab-org/gitlab/-/issues/361241#other-cleanup-tasks
|
||||
skip_update_example_snapshot_prosemirror_json: "skipping because JSON isn't cool enough"
|
||||
03_01_00__first_gitlab_specific_section_with_examples__strong_but_with_two_asterisks__001:
|
||||
skip_update_example_snapshot_html_static: "skipping because there's too much static"
|
||||
|
|
|
@ -27,19 +27,6 @@ RSpec.describe Ci::BuildFinishedWorker do
|
|||
subject
|
||||
end
|
||||
|
||||
context 'when the execute_build_hooks_inline feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(execute_build_hooks_inline: false)
|
||||
end
|
||||
|
||||
it 'uses the BuildHooksWorker' do
|
||||
expect(build).not_to receive(:execute_hooks)
|
||||
expect(BuildHooksWorker).to receive(:perform_async).with(build)
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'when build is failed' do
|
||||
before do
|
||||
build.update!(status: :failed)
|
||||
|
|
Loading…
Reference in New Issue