Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
c01895daab
commit
cf73ed7d11
|
@ -1,9 +1,7 @@
|
|||
/* eslint-disable class-methods-use-this */
|
||||
/* eslint-disable @gitlab/require-i18n-strings */
|
||||
|
||||
import { defaultMarkdownSerializer } from 'prosemirror-markdown';
|
||||
import { Node } from 'tiptap';
|
||||
import { HIGHER_PARSE_RULE_PRIORITY } from '../constants';
|
||||
|
||||
/**
|
||||
* Abstract base class for playable media, like video and audio.
|
||||
|
@ -32,34 +30,34 @@ export default class Playable extends Node {
|
|||
|
||||
const parseDOM = [
|
||||
{
|
||||
tag: `.${this.mediaType}-container`,
|
||||
skip: true,
|
||||
},
|
||||
{
|
||||
tag: `.${this.mediaType}-container p`,
|
||||
priority: HIGHER_PARSE_RULE_PRIORITY,
|
||||
ignore: true,
|
||||
},
|
||||
{
|
||||
tag: `${this.mediaType}[src]`,
|
||||
getAttrs: (el) => ({ src: el.src, alt: el.dataset.title }),
|
||||
tag: `.media-container`,
|
||||
getAttrs: (el) => ({
|
||||
src: el.querySelector('audio,video').src,
|
||||
alt: el.querySelector('audio,video').dataset.title,
|
||||
}),
|
||||
},
|
||||
];
|
||||
|
||||
const toDOM = (node) => [
|
||||
this.mediaType,
|
||||
{
|
||||
src: node.attrs.src,
|
||||
controls: true,
|
||||
'data-setup': '{}',
|
||||
'data-title': node.attrs.alt,
|
||||
...this.extraElementAttrs,
|
||||
},
|
||||
'span',
|
||||
{ class: 'media-container' },
|
||||
[
|
||||
this.options.mediaType,
|
||||
{
|
||||
src: node.attrs.src,
|
||||
controls: true,
|
||||
'data-setup': '{}',
|
||||
'data-title': node.attrs.alt,
|
||||
...this.extraElementAttrs,
|
||||
},
|
||||
],
|
||||
['a', { href: node.attrs.src }, node.attrs.alt],
|
||||
];
|
||||
|
||||
return {
|
||||
attrs,
|
||||
group: 'block',
|
||||
group: 'inline',
|
||||
inline: true,
|
||||
draggable: true,
|
||||
parseDOM,
|
||||
toDOM,
|
||||
|
@ -68,6 +66,5 @@ export default class Playable extends Node {
|
|||
|
||||
toMarkdown(state, node) {
|
||||
defaultMarkdownSerializer.nodes.image(state, node);
|
||||
state.closeBlock(node);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import Playable from './playable';
|
||||
|
||||
export default Playable.extend({
|
||||
defaultOptions: {
|
||||
...Playable.options,
|
||||
mediaType: 'audio',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,71 @@
|
|||
import { Node } from '@tiptap/core';
|
||||
|
||||
const queryPlayableElement = (element, mediaType) => element.querySelector(mediaType);
|
||||
|
||||
export default Node.create({
|
||||
name: 'playable',
|
||||
group: 'inline',
|
||||
inline: true,
|
||||
draggable: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
src: {
|
||||
default: null,
|
||||
parseHTML: (element) => {
|
||||
const playable = queryPlayableElement(element, this.options.mediaType);
|
||||
|
||||
return {
|
||||
src: playable.src,
|
||||
};
|
||||
},
|
||||
},
|
||||
canonicalSrc: {
|
||||
default: null,
|
||||
parseHTML: (element) => {
|
||||
const playable = queryPlayableElement(element, this.options.mediaType);
|
||||
|
||||
return {
|
||||
canonicalSrc: playable.dataset.canonicalSrc,
|
||||
};
|
||||
},
|
||||
},
|
||||
alt: {
|
||||
default: null,
|
||||
parseHTML: (element) => {
|
||||
const playable = queryPlayableElement(element, this.options.mediaType);
|
||||
|
||||
return {
|
||||
alt: playable.dataset.title,
|
||||
};
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: '.media-container',
|
||||
},
|
||||
];
|
||||
},
|
||||
|
||||
renderHTML({ node }) {
|
||||
return [
|
||||
'span',
|
||||
{ class: 'media-container' },
|
||||
[
|
||||
this.options.mediaType,
|
||||
{
|
||||
src: node.attrs.src,
|
||||
controls: true,
|
||||
'data-setup': '{}',
|
||||
'data-title': node.attrs.alt,
|
||||
...this.extraElementAttrs,
|
||||
},
|
||||
],
|
||||
['a', { href: node.attrs.src }, node.attrs.alt],
|
||||
];
|
||||
},
|
||||
});
|
|
@ -2,6 +2,7 @@ import { Editor } from '@tiptap/vue-2';
|
|||
import { isFunction } from 'lodash';
|
||||
import { PROVIDE_SERIALIZER_OR_RENDERER_ERROR } from '../constants';
|
||||
import Attachment from '../extensions/attachment';
|
||||
import Audio from '../extensions/audio';
|
||||
import Blockquote from '../extensions/blockquote';
|
||||
import Bold from '../extensions/bold';
|
||||
import BulletList from '../extensions/bullet_list';
|
||||
|
@ -63,6 +64,7 @@ export const createContentEditor = ({
|
|||
|
||||
const builtInContentEditorExtensions = [
|
||||
Attachment.configure({ uploadsPath, renderMarkdown }),
|
||||
Audio,
|
||||
Blockquote,
|
||||
Bold,
|
||||
BulletList,
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
defaultMarkdownSerializer,
|
||||
} from 'prosemirror-markdown/src/to_markdown';
|
||||
import { DOMParser as ProseMirrorDOMParser } from 'prosemirror-model';
|
||||
import Audio from '../extensions/audio';
|
||||
import Blockquote from '../extensions/blockquote';
|
||||
import Bold from '../extensions/bold';
|
||||
import BulletList from '../extensions/bullet_list';
|
||||
|
@ -40,6 +41,8 @@ import {
|
|||
openTag,
|
||||
closeTag,
|
||||
renderOrderedList,
|
||||
renderImage,
|
||||
renderPlayable,
|
||||
} from './serialization_helpers';
|
||||
|
||||
const defaultSerializerConfig = {
|
||||
|
@ -92,6 +95,7 @@ const defaultSerializerConfig = {
|
|||
},
|
||||
|
||||
nodes: {
|
||||
[Audio.name]: renderPlayable,
|
||||
[Blockquote.name]: (state, node) => {
|
||||
if (node.attrs.multiline) {
|
||||
state.write('>>>');
|
||||
|
@ -120,12 +124,7 @@ const defaultSerializerConfig = {
|
|||
[HardBreak.name]: renderHardBreak,
|
||||
[Heading.name]: defaultMarkdownSerializer.nodes.heading,
|
||||
[HorizontalRule.name]: defaultMarkdownSerializer.nodes.horizontal_rule,
|
||||
[Image.name]: (state, node) => {
|
||||
const { alt, canonicalSrc, src, title } = node.attrs;
|
||||
const quotedTitle = title ? ` ${state.quote(title)}` : '';
|
||||
|
||||
state.write(`![${state.esc(alt || '')}](${state.esc(canonicalSrc || src)}${quotedTitle})`);
|
||||
},
|
||||
[Image.name]: renderImage,
|
||||
[ListItem.name]: defaultMarkdownSerializer.nodes.list_item,
|
||||
[OrderedList.name]: renderOrderedList,
|
||||
[Paragraph.name]: defaultMarkdownSerializer.nodes.paragraph,
|
||||
|
|
|
@ -286,3 +286,14 @@ export function renderHardBreak(state, node, parent, index) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function renderImage(state, node) {
|
||||
const { alt, canonicalSrc, src, title } = node.attrs;
|
||||
const quotedTitle = title ? ` ${state.quote(title)}` : '';
|
||||
|
||||
state.write(`![${state.esc(alt || '')}](${state.esc(canonicalSrc || src)}${quotedTitle})`);
|
||||
}
|
||||
|
||||
export function renderPlayable(state, node) {
|
||||
renderImage(state, node);
|
||||
}
|
||||
|
|
|
@ -41,6 +41,12 @@
|
|||
}
|
||||
}
|
||||
|
||||
.media-container {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: $gl-spacing-scale-2;
|
||||
}
|
||||
|
||||
img:not(.emoji) {
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
|
|
|
@ -58,8 +58,8 @@ class CommitStatus < Ci::ApplicationRecord
|
|||
scope :in_pipelines, ->(pipelines) { where(pipeline: pipelines) }
|
||||
scope :eager_load_pipeline, -> { eager_load(:pipeline, project: { namespace: :route }) }
|
||||
scope :with_pipeline, -> { joins(:pipeline) }
|
||||
scope :updated_at_before, ->(date) { where('ci_builds.updated_at < ?', date) }
|
||||
scope :created_at_before, ->(date) { where('ci_builds.created_at < ?', date) }
|
||||
scope :updated_at_before, ->(date) { where('ci_builds.updated_at < ?', date) }
|
||||
scope :updated_before, ->(lookback:, timeout:) {
|
||||
where('(ci_builds.created_at BETWEEN ? AND ?) AND (ci_builds.updated_at BETWEEN ? AND ?)', lookback, timeout, lookback, timeout)
|
||||
}
|
||||
|
|
|
@ -63,10 +63,12 @@ class StuckCiJobsWorker # rubocop:disable Scalability/IdempotentWorker
|
|||
end
|
||||
|
||||
def running_timed_out_builds
|
||||
Ci::Build.running.where( # rubocop: disable CodeReuse/ActiveRecord
|
||||
'ci_builds.updated_at < ?',
|
||||
BUILD_RUNNING_OUTDATED_TIMEOUT.ago
|
||||
)
|
||||
if Feature.enabled?(:ci_new_query_for_running_stuck_jobs)
|
||||
running_builds = Ci::Build.running.created_at_before(BUILD_RUNNING_OUTDATED_TIMEOUT.ago).order(created_at: :asc, project_id: :asc) # rubocop: disable CodeReuse/ActiveRecord
|
||||
Ci::Build.id_in(running_builds).updated_at_before(BUILD_RUNNING_OUTDATED_TIMEOUT.ago)
|
||||
else
|
||||
Ci::Build.running.updated_at_before(BUILD_RUNNING_OUTDATED_TIMEOUT.ago)
|
||||
end
|
||||
end
|
||||
|
||||
def try_obtain_lease
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
name: ci_new_query_for_running_stuck_jobs
|
||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/68891
|
||||
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/339264
|
||||
milestone: '14.3'
|
||||
type: development
|
||||
group: group::pipeline execution
|
||||
default_enabled: false
|
|
@ -52,7 +52,7 @@ module Banzai
|
|||
doc.document.create_element(media_type, media_element_attrs)
|
||||
end
|
||||
|
||||
def download_paragraph(doc, element)
|
||||
def download_link(doc, element)
|
||||
link_content = element['title'] || element['alt']
|
||||
|
||||
link_element_attrs = {
|
||||
|
@ -67,19 +67,15 @@ module Banzai
|
|||
link_element_attrs['data-canonical-src'] = element['data-canonical-src']
|
||||
end
|
||||
|
||||
link = doc.document.create_element('a', link_content, link_element_attrs)
|
||||
|
||||
doc.document.create_element('p').tap do |paragraph|
|
||||
paragraph.children = link
|
||||
end
|
||||
doc.document.create_element('a', link_content, link_element_attrs)
|
||||
end
|
||||
|
||||
def media_node(doc, element)
|
||||
container_element_attrs = { class: "#{media_type}-container" }
|
||||
container_element_attrs = { class: "media-container #{media_type}-container" }
|
||||
|
||||
doc.document.create_element( "div", container_element_attrs).tap do |container|
|
||||
doc.document.create_element('span', container_element_attrs).tap do |container|
|
||||
container.add_child(media_element(doc, element))
|
||||
container.add_child(download_paragraph(doc, element))
|
||||
container.add_child(download_link(doc, element))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -145,3 +145,15 @@
|
|||
context: project_wiki
|
||||
markdown: |-
|
||||
Hi @gitlab - thank you for reporting this ~bug (#1) we hope to fix it in %1.1 as part of !1
|
||||
- name: audio
|
||||
markdown: '![Sample Audio](https://gitlab.com/gitlab.mp3)'
|
||||
- name: audio_in_lists
|
||||
markdown: |-
|
||||
* ![Sample Audio](https://gitlab.com/1.mp3)
|
||||
* ![Sample Audio](https://gitlab.com/2.mp3)
|
||||
|
||||
1. ![Sample Audio](https://gitlab.com/1.mp3)
|
||||
2. ![Sample Audio](https://gitlab.com/2.mp3)
|
||||
|
||||
* [x] ![Sample Audio](https://gitlab.com/1.mp3)
|
||||
* [x] ![Sample Audio](https://gitlab.com/2.mp3)
|
||||
|
|
|
@ -25,18 +25,14 @@ RSpec.describe Banzai::Filter::AudioLinkFilter do
|
|||
it 'replaces the image tag with an audio tag' do
|
||||
container = filter(image).children.first
|
||||
|
||||
expect(container.name).to eq 'div'
|
||||
expect(container['class']).to eq 'audio-container'
|
||||
expect(container.name).to eq 'span'
|
||||
expect(container['class']).to eq 'media-container audio-container'
|
||||
|
||||
audio, paragraph = container.children
|
||||
audio, link = container.children
|
||||
|
||||
expect(audio.name).to eq 'audio'
|
||||
expect(audio['src']).to eq src
|
||||
|
||||
expect(paragraph.name).to eq 'p'
|
||||
|
||||
link = paragraph.children.first
|
||||
|
||||
expect(link.name).to eq 'a'
|
||||
expect(link['href']).to eq src
|
||||
expect(link['target']).to eq '_blank'
|
||||
|
@ -105,15 +101,13 @@ RSpec.describe Banzai::Filter::AudioLinkFilter do
|
|||
image = %(<img src="#{proxy_src}" data-canonical-src="#{canonical_src}"/>)
|
||||
container = filter(image).children.first
|
||||
|
||||
expect(container['class']).to eq 'audio-container'
|
||||
expect(container['class']).to eq 'media-container audio-container'
|
||||
|
||||
audio, paragraph = container.children
|
||||
audio, link = container.children
|
||||
|
||||
expect(audio['src']).to eq proxy_src
|
||||
expect(audio['data-canonical-src']).to eq canonical_src
|
||||
|
||||
link = paragraph.children.first
|
||||
|
||||
expect(link['href']).to eq proxy_src
|
||||
end
|
||||
end
|
||||
|
|
|
@ -25,20 +25,16 @@ RSpec.describe Banzai::Filter::VideoLinkFilter do
|
|||
it 'replaces the image tag with a video tag' do
|
||||
container = filter(image).children.first
|
||||
|
||||
expect(container.name).to eq 'div'
|
||||
expect(container['class']).to eq 'video-container'
|
||||
expect(container.name).to eq 'span'
|
||||
expect(container['class']).to eq 'media-container video-container'
|
||||
|
||||
video, paragraph = container.children
|
||||
video, link = container.children
|
||||
|
||||
expect(video.name).to eq 'video'
|
||||
expect(video['src']).to eq src
|
||||
expect(video['width']).to eq "400"
|
||||
expect(video['preload']).to eq 'metadata'
|
||||
|
||||
expect(paragraph.name).to eq 'p'
|
||||
|
||||
link = paragraph.children.first
|
||||
|
||||
expect(link.name).to eq 'a'
|
||||
expect(link['href']).to eq src
|
||||
expect(link['target']).to eq '_blank'
|
||||
|
@ -107,15 +103,13 @@ RSpec.describe Banzai::Filter::VideoLinkFilter do
|
|||
image = %(<img src="#{proxy_src}" data-canonical-src="#{canonical_src}"/>)
|
||||
container = filter(image).children.first
|
||||
|
||||
expect(container['class']).to eq 'video-container'
|
||||
expect(container['class']).to eq 'media-container video-container'
|
||||
|
||||
video, paragraph = container.children
|
||||
video, link = container.children
|
||||
|
||||
expect(video['src']).to eq proxy_src
|
||||
expect(video['data-canonical-src']).to eq canonical_src
|
||||
|
||||
link = paragraph.children.first
|
||||
|
||||
expect(link['href']).to eq proxy_src
|
||||
end
|
||||
end
|
||||
|
|
|
@ -186,20 +186,32 @@ RSpec.describe StuckCiJobsWorker do
|
|||
end
|
||||
end
|
||||
|
||||
context 'when job is running' do
|
||||
let(:status) { 'running' }
|
||||
shared_examples 'job is running' do
|
||||
context 'when job is running' do
|
||||
let(:status) { 'running' }
|
||||
|
||||
context 'when job was updated_at more than an hour ago' do
|
||||
let(:updated_at) { 2.hours.ago }
|
||||
context 'when job was updated_at more than an hour ago' do
|
||||
let(:updated_at) { 2.hours.ago }
|
||||
|
||||
it_behaves_like 'job is dropped'
|
||||
it_behaves_like 'job is dropped'
|
||||
end
|
||||
|
||||
context 'when job was updated in less than 1 hour ago' do
|
||||
let(:updated_at) { 30.minutes.ago }
|
||||
|
||||
it_behaves_like 'job is unchanged'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'job is running'
|
||||
|
||||
context 'when ci_new_query_for_running_stuck_jobs feature flag is disabled' do
|
||||
before do
|
||||
stub_feature_flags(ci_new_query_for_running_stuck_jobs: false)
|
||||
end
|
||||
|
||||
context 'when job was updated in less than 1 hour ago' do
|
||||
let(:updated_at) { 30.minutes.ago }
|
||||
|
||||
it_behaves_like 'job is unchanged'
|
||||
end
|
||||
it_behaves_like 'job is running'
|
||||
end
|
||||
|
||||
%w(success skipped failed canceled).each do |status|
|
||||
|
|
Loading…
Reference in New Issue