Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
0a209fd10e
commit
3bd9ad5574
61 changed files with 976 additions and 1104 deletions
3
Gemfile
3
Gemfile
|
@ -238,9 +238,6 @@ gem 'redis-rails', '~> 5.0.2'
|
|||
# Discord integration
|
||||
gem 'discordrb-webhooks', '~> 3.4', require: false
|
||||
|
||||
# HipChat integration
|
||||
gem 'hipchat', '~> 1.5.0'
|
||||
|
||||
# Jira integration
|
||||
gem 'jira-ruby', '~> 2.1.4'
|
||||
gem 'atlassian-jwt', '~> 0.2.0'
|
||||
|
|
|
@ -593,9 +593,6 @@ GEM
|
|||
railties (>= 5.0)
|
||||
heapy (0.2.0)
|
||||
thor
|
||||
hipchat (1.5.2)
|
||||
httparty
|
||||
mimemagic
|
||||
html-pipeline (2.13.2)
|
||||
activesupport (>= 2)
|
||||
nokogiri (>= 1.4)
|
||||
|
@ -1456,7 +1453,6 @@ DEPENDENCIES
|
|||
hashie
|
||||
hashie-forbidden_attributes
|
||||
health_check (~> 3.0)
|
||||
hipchat (~> 1.5.0)
|
||||
html-pipeline (~> 2.13.2)
|
||||
html2text
|
||||
httparty (~> 0.16.4)
|
||||
|
|
|
@ -0,0 +1,253 @@
|
|||
<script>
|
||||
import { GlForm, GlIcon, GlLink, GlButton, GlSprintf } from '@gitlab/ui';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
import { setUrlFragment } from '~/lib/utils/url_utility';
|
||||
import { __, s__, sprintf } from '~/locale';
|
||||
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
|
||||
|
||||
const MARKDOWN_LINK_TEXT = {
|
||||
markdown: '[Link Title](page-slug)',
|
||||
rdoc: '{Link title}[link:page-slug]',
|
||||
asciidoc: 'link:page-slug[Link title]',
|
||||
org: '[[page-slug]]',
|
||||
};
|
||||
|
||||
export default {
|
||||
components: {
|
||||
GlForm,
|
||||
GlSprintf,
|
||||
GlIcon,
|
||||
GlLink,
|
||||
GlButton,
|
||||
MarkdownField,
|
||||
},
|
||||
inject: ['formatOptions', 'pageInfo'],
|
||||
data() {
|
||||
return {
|
||||
title: this.pageInfo.title?.trim() || '',
|
||||
format: this.pageInfo.format || 'markdown',
|
||||
content: this.pageInfo.content?.trim() || '',
|
||||
commitMessage: '',
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
csrfToken() {
|
||||
return csrf.token;
|
||||
},
|
||||
formAction() {
|
||||
return this.pageInfo.persisted ? this.pageInfo.path : this.pageInfo.createPath;
|
||||
},
|
||||
helpPath() {
|
||||
return setUrlFragment(
|
||||
this.pageInfo.helpPath,
|
||||
this.pageInfo.persisted ? 'moving-a-wiki-page' : 'creating-a-new-wiki-page',
|
||||
);
|
||||
},
|
||||
commitMessageI18n() {
|
||||
return this.pageInfo.persisted
|
||||
? s__('WikiPage|Update %{pageTitle}')
|
||||
: s__('WikiPage|Create %{pageTitle}');
|
||||
},
|
||||
linkExample() {
|
||||
return MARKDOWN_LINK_TEXT[this.format];
|
||||
},
|
||||
submitButtonText() {
|
||||
if (this.pageInfo.persisted) return __('Save changes');
|
||||
return s__('WikiPage|Create page');
|
||||
},
|
||||
cancelFormPath() {
|
||||
if (this.pageInfo.persisted) return this.pageInfo.path;
|
||||
return this.pageInfo.wikiPath;
|
||||
},
|
||||
wikiSpecificMarkdownHelpPath() {
|
||||
return setUrlFragment(this.pageInfo.markdownHelpPath, 'wiki-specific-markdown');
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.updateCommitMessage();
|
||||
},
|
||||
methods: {
|
||||
handleFormSubmit() {
|
||||
window.removeEventListener('beforeunload', this.onBeforeUnload);
|
||||
},
|
||||
|
||||
handleContentChange() {
|
||||
window.addEventListener('beforeunload', this.onBeforeUnload);
|
||||
},
|
||||
|
||||
onBeforeUnload() {
|
||||
return '';
|
||||
},
|
||||
|
||||
updateCommitMessage() {
|
||||
if (!this.title) return;
|
||||
|
||||
// Replace hyphens with spaces
|
||||
const newTitle = this.title.replace(/-+/g, ' ');
|
||||
|
||||
const newCommitMessage = sprintf(this.commitMessageI18n, { pageTitle: newTitle }, false);
|
||||
this.commitMessage = newCommitMessage;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-form
|
||||
:action="formAction"
|
||||
method="post"
|
||||
class="wiki-form common-note-form gl-mt-3 js-quick-submit"
|
||||
@submit="handleFormSubmit"
|
||||
>
|
||||
<input :value="csrfToken" type="hidden" name="authenticity_token" />
|
||||
<input v-if="pageInfo.persisted" type="hidden" name="_method" value="put" />
|
||||
<input
|
||||
:v-if="pageInfo.persisted"
|
||||
type="hidden"
|
||||
name="wiki[last_commit_sha]"
|
||||
:value="pageInfo.lastCommitSha"
|
||||
/>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-2 col-form-label">
|
||||
<label class="control-label-full-width" for="wiki_title">{{ s__('WikiPage|Title') }}</label>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
id="wiki_title"
|
||||
v-model.trim="title"
|
||||
name="wiki[title]"
|
||||
type="text"
|
||||
class="form-control"
|
||||
data-qa-selector="wiki_title_textbox"
|
||||
:required="true"
|
||||
:autofocus="!pageInfo.persisted"
|
||||
:placeholder="s__('WikiPage|Page title')"
|
||||
@input="updateCommitMessage"
|
||||
/>
|
||||
<span class="gl-display-inline-block gl-max-w-full gl-mt-2 gl-text-gray-600">
|
||||
<gl-icon class="gl-mr-n1" name="bulb" />
|
||||
{{
|
||||
pageInfo.persisted
|
||||
? s__(
|
||||
'WikiPage|Tip: You can move this page by adding the path to the beginning of the title.',
|
||||
)
|
||||
: s__(
|
||||
'WikiPage|Tip: You can specify the full path for the new file. We will automatically create any missing directories.',
|
||||
)
|
||||
}}
|
||||
<gl-link :href="helpPath" target="_blank" data-testid="wiki-title-help-link"
|
||||
><gl-icon name="question-o" /> {{ __('More Information.') }}</gl-link
|
||||
>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-2 col-form-label">
|
||||
<label class="control-label-full-width" for="wiki_format">{{
|
||||
s__('WikiPage|Format')
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<select id="wiki_format" v-model="format" class="form-control" name="wiki[format]">
|
||||
<option v-for="(key, label) of formatOptions" :key="key" :value="key">
|
||||
{{ label }}
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-2 col-form-label">
|
||||
<label class="control-label-full-width" for="wiki_content">{{
|
||||
s__('WikiPage|Content')
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<markdown-field
|
||||
:markdown-preview-path="pageInfo.markdownPreviewPath"
|
||||
:can-attach-file="true"
|
||||
:enable-autocomplete="true"
|
||||
:textarea-value="content"
|
||||
:markdown-docs-path="pageInfo.markdownHelpPath"
|
||||
:uploads-path="pageInfo.uploadsPath"
|
||||
class="bordered-box"
|
||||
>
|
||||
<template #textarea>
|
||||
<textarea
|
||||
id="wiki_content"
|
||||
ref="textarea"
|
||||
v-model.trim="content"
|
||||
name="wiki[content]"
|
||||
class="note-textarea js-gfm-input js-autosize markdown-area"
|
||||
dir="auto"
|
||||
data-supports-quick-actions="false"
|
||||
data-qa-selector="wiki_content_textarea"
|
||||
:autofocus="pageInfo.persisted"
|
||||
:aria-label="s__('WikiPage|Content')"
|
||||
:placeholder="s__('WikiPage|Write your content or drag files here…')"
|
||||
@input="handleContentChange"
|
||||
>
|
||||
</textarea>
|
||||
</template>
|
||||
</markdown-field>
|
||||
<div class="clearfix"></div>
|
||||
<div class="error-alert"></div>
|
||||
|
||||
<div class="form-text gl-text-gray-600">
|
||||
<gl-sprintf
|
||||
:message="
|
||||
s__(
|
||||
'WikiPage|To link to a (new) page, simply type %{linkExample}. More examples are in the %{linkStart}documentation%{linkEnd}.',
|
||||
)
|
||||
"
|
||||
>
|
||||
<template #linkExample
|
||||
><code>{{ linkExample }}</code></template
|
||||
>
|
||||
<template
|
||||
#link="// eslint-disable-next-line vue/no-template-shadow
|
||||
{ content }"
|
||||
><gl-link
|
||||
:href="wikiSpecificMarkdownHelpPath"
|
||||
target="_blank"
|
||||
data-testid="wiki-markdown-help-link"
|
||||
>{{ content }}</gl-link
|
||||
></template
|
||||
>
|
||||
</gl-sprintf>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-2 col-form-label">
|
||||
<label class="control-label-full-width" for="wiki_message">{{
|
||||
s__('WikiPage|Commit message')
|
||||
}}</label>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<input
|
||||
id="wiki_message"
|
||||
v-model.trim="commitMessage"
|
||||
name="wiki[message]"
|
||||
type="text"
|
||||
class="form-control"
|
||||
data-qa-selector="wiki_message_textbox"
|
||||
:placeholder="s__('WikiPage|Commit message')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<gl-button
|
||||
category="primary"
|
||||
variant="confirm"
|
||||
type="submit"
|
||||
data-qa-selector="wiki_submit_button"
|
||||
data-testid="wiki-submit-button"
|
||||
:disabled="!content || !title"
|
||||
>{{ submitButtonText }}</gl-button
|
||||
>
|
||||
<gl-button :href="cancelFormPath" class="float-right" data-testid="wiki-cancel-button">{{
|
||||
__('Cancel')
|
||||
}}</gl-button>
|
||||
</div>
|
||||
</gl-form>
|
||||
</template>
|
|
@ -1,12 +1,14 @@
|
|||
import $ from 'jquery';
|
||||
import Vue from 'vue';
|
||||
import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki';
|
||||
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
import Translate from '~/vue_shared/translate';
|
||||
import GLForm from '../../../gl_form';
|
||||
import ZenMode from '../../../zen_mode';
|
||||
import deleteWikiModal from './components/delete_wiki_modal.vue';
|
||||
import wikiAlert from './components/wiki_alert.vue';
|
||||
import wikiForm from './components/wiki_form.vue';
|
||||
import Wikis from './wikis';
|
||||
|
||||
const createModalVueApp = () => {
|
||||
|
@ -61,7 +63,28 @@ const createAlertVueApp = () => {
|
|||
}
|
||||
};
|
||||
|
||||
const createWikiFormApp = () => {
|
||||
const el = document.getElementById('js-wiki-form');
|
||||
|
||||
if (el) {
|
||||
const { pageInfo, formatOptions } = el.dataset;
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el,
|
||||
provide: {
|
||||
formatOptions: JSON.parse(formatOptions),
|
||||
pageInfo: convertObjectPropsToCamelCase(JSON.parse(pageInfo)),
|
||||
},
|
||||
render(createElement) {
|
||||
return createElement(wikiForm);
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default () => {
|
||||
createModalVueApp();
|
||||
createAlertVueApp();
|
||||
createWikiFormApp();
|
||||
};
|
||||
|
|
|
@ -1,15 +1,7 @@
|
|||
import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils';
|
||||
import { s__, sprintf } from '~/locale';
|
||||
import Tracking from '~/tracking';
|
||||
import showToast from '~/vue_shared/plugins/global_toast';
|
||||
|
||||
const MARKDOWN_LINK_TEXT = {
|
||||
markdown: '[Link Title](page-slug)',
|
||||
rdoc: '{Link title}[link:page-slug]',
|
||||
asciidoc: 'link:page-slug[Link title]',
|
||||
org: '[[page-slug]]',
|
||||
};
|
||||
|
||||
const TRACKING_EVENT_NAME = 'view_wiki_page';
|
||||
const TRACKING_CONTEXT_SCHEMA = 'iglu:com.gitlab/wiki_page_context/jsonschema/1-0-1';
|
||||
|
||||
|
@ -23,78 +15,11 @@ export default class Wikis {
|
|||
sidebarToggles[i].addEventListener('click', (e) => this.handleToggleSidebar(e));
|
||||
}
|
||||
|
||||
this.isNewWikiPage = Boolean(document.querySelector('.js-new-wiki-page'));
|
||||
this.editTitleInput = document.querySelector('form.wiki-form #wiki_title');
|
||||
this.commitMessageInput = document.querySelector('form.wiki-form #wiki_message');
|
||||
this.submitButton = document.querySelector('.js-wiki-btn-submit');
|
||||
this.commitMessageI18n = this.isNewWikiPage
|
||||
? s__('WikiPageCreate|Create %{pageTitle}')
|
||||
: s__('WikiPageEdit|Update %{pageTitle}');
|
||||
|
||||
if (this.editTitleInput) {
|
||||
// Initialize the commit message on load
|
||||
if (this.editTitleInput.value) this.setWikiCommitMessage(this.editTitleInput.value);
|
||||
|
||||
// Set the commit message as the page title is changed
|
||||
this.editTitleInput.addEventListener('keyup', (e) => this.handleWikiTitleChange(e));
|
||||
}
|
||||
|
||||
window.addEventListener('resize', () => this.renderSidebar());
|
||||
this.renderSidebar();
|
||||
|
||||
const changeFormatSelect = document.querySelector('#wiki_format');
|
||||
const linkExample = document.querySelector('.js-markup-link-example');
|
||||
|
||||
if (changeFormatSelect) {
|
||||
changeFormatSelect.addEventListener('change', (e) => {
|
||||
linkExample.innerHTML = MARKDOWN_LINK_TEXT[e.target.value];
|
||||
});
|
||||
}
|
||||
|
||||
this.wikiTextarea = document.querySelector('form.wiki-form #wiki_content');
|
||||
const wikiForm = document.querySelector('form.wiki-form');
|
||||
|
||||
if (this.wikiTextarea) {
|
||||
this.wikiTextarea.addEventListener('input', () => this.handleWikiContentChange());
|
||||
|
||||
wikiForm.addEventListener('submit', () => {
|
||||
window.onbeforeunload = null;
|
||||
});
|
||||
}
|
||||
|
||||
Wikis.trackPageView();
|
||||
Wikis.showToasts();
|
||||
|
||||
this.updateSubmitButton();
|
||||
}
|
||||
|
||||
handleWikiContentChange() {
|
||||
this.updateSubmitButton();
|
||||
|
||||
window.onbeforeunload = () => '';
|
||||
}
|
||||
|
||||
handleWikiTitleChange(e) {
|
||||
this.updateSubmitButton();
|
||||
this.setWikiCommitMessage(e.target.value);
|
||||
}
|
||||
|
||||
updateSubmitButton() {
|
||||
if (!this.wikiTextarea) return;
|
||||
|
||||
const isEnabled = Boolean(this.wikiTextarea.value.trim() && this.editTitleInput.value.trim());
|
||||
if (isEnabled) this.submitButton.removeAttribute('disabled');
|
||||
else this.submitButton.setAttribute('disabled', 'true');
|
||||
}
|
||||
|
||||
setWikiCommitMessage(rawTitle) {
|
||||
let title = rawTitle;
|
||||
|
||||
// Replace hyphens with spaces
|
||||
if (title) title = title.replace(/-+/g, ' ');
|
||||
|
||||
const newCommitMessage = sprintf(this.commitMessageI18n, { pageTitle: title }, false);
|
||||
this.commitMessageInput.value = newCommitMessage;
|
||||
}
|
||||
|
||||
handleToggleSidebar(e) {
|
||||
|
|
|
@ -39,7 +39,7 @@ function formatTooltipText({ date, count }) {
|
|||
if (count > 0) {
|
||||
contribText = n__('%d contribution', '%d contributions', count);
|
||||
}
|
||||
return `${contribText}<br />${dateDayName} ${dateText}`;
|
||||
return `${contribText}<br /><span class="gl-text-gray-300">${dateDayName} ${dateText}</span>`;
|
||||
}
|
||||
|
||||
const initColorKey = () => d3.scaleLinear().range(['#acd5f2', '#254e77']).domain([0, 3]);
|
||||
|
|
|
@ -62,6 +62,11 @@ export default {
|
|||
required: false,
|
||||
default: true,
|
||||
},
|
||||
uploadsPath: {
|
||||
type: String,
|
||||
required: false,
|
||||
default: '',
|
||||
},
|
||||
enableAutocomplete: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
|
@ -229,6 +234,7 @@ export default {
|
|||
ref="gl-form"
|
||||
:class="{ 'gl-mt-3 gl-mb-3': addSpacingClasses }"
|
||||
class="js-vue-markdown-field md-area position-relative gfm-form"
|
||||
:data-uploads-path="uploadsPath"
|
||||
>
|
||||
<markdown-header
|
||||
:preview-markdown="previewMarkdown"
|
||||
|
|
|
@ -191,7 +191,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
|
|||
end
|
||||
|
||||
def disable_query_limiting
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/63107')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/29418')
|
||||
end
|
||||
|
||||
def application_setting_params
|
||||
|
|
|
@ -43,7 +43,7 @@ class Import::GitlabProjectsController < Import::BaseController
|
|||
end
|
||||
|
||||
def disable_query_limiting
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20823')
|
||||
end
|
||||
|
||||
def uploader_class
|
||||
|
|
|
@ -116,6 +116,6 @@ class Import::ManifestController < Import::BaseController
|
|||
end
|
||||
|
||||
def disable_query_limiting
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/48939')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/23147')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -119,7 +119,7 @@ class Projects::ForksController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def disable_query_limiting
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42335')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20783')
|
||||
end
|
||||
|
||||
def load_namespaces_with_associations
|
||||
|
|
|
@ -356,10 +356,10 @@ class Projects::IssuesController < Projects::ApplicationController
|
|||
def disable_query_limiting
|
||||
# Also see the following issues:
|
||||
#
|
||||
# 1. https://gitlab.com/gitlab-org/gitlab-foss/issues/42423
|
||||
# 2. https://gitlab.com/gitlab-org/gitlab-foss/issues/42424
|
||||
# 3. https://gitlab.com/gitlab-org/gitlab-foss/issues/42426
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42422')
|
||||
# 1. https://gitlab.com/gitlab-org/gitlab/-/issues/20815
|
||||
# 2. https://gitlab.com/gitlab-org/gitlab/-/issues/20816
|
||||
# 3. https://gitlab.com/gitlab-org/gitlab/-/issues/21068
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20814')
|
||||
end
|
||||
|
||||
private
|
||||
|
|
|
@ -134,7 +134,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
|
|||
# rubocop: enable CodeReuse/ActiveRecord
|
||||
|
||||
def disable_query_limiting
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42384')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20801')
|
||||
end
|
||||
|
||||
def incr_count_webide_merge_request
|
||||
|
|
|
@ -470,8 +470,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
end
|
||||
|
||||
def disable_query_limiting
|
||||
# Also see https://gitlab.com/gitlab-org/gitlab-foss/issues/42441
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42438')
|
||||
# Also see https://gitlab.com/gitlab-org/gitlab/-/issues/20827
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20824')
|
||||
end
|
||||
|
||||
def reports_response(report_comparison, pipeline = nil)
|
||||
|
|
|
@ -43,6 +43,6 @@ class Projects::NetworkController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def disable_query_limiting
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42333')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20782')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -88,6 +88,6 @@ class Projects::NotesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def disable_query_limiting
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42383')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20800')
|
||||
end
|
||||
end
|
||||
|
|
|
@ -272,8 +272,8 @@ class Projects::PipelinesController < Projects::ApplicationController
|
|||
end
|
||||
|
||||
def disable_query_limiting
|
||||
# Also see https://gitlab.com/gitlab-org/gitlab-foss/issues/42343
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42339')
|
||||
# Also see https://gitlab.com/gitlab-org/gitlab/-/issues/20785
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20784')
|
||||
end
|
||||
|
||||
def authorize_update_pipeline!
|
||||
|
|
|
@ -163,7 +163,7 @@ class RegistrationsController < Devise::RegistrationsController
|
|||
end
|
||||
|
||||
def disable_query_limiting
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42380')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20798')
|
||||
end
|
||||
|
||||
def load_recaptcha
|
||||
|
|
|
@ -11,7 +11,7 @@ module Mutations
|
|||
description: 'The project to move the issue to.'
|
||||
|
||||
def resolve(project_path:, iid:, target_project_path:)
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/267762')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20816')
|
||||
|
||||
issue = authorized_find!(project_path: project_path, iid: iid)
|
||||
source_project = issue.project
|
||||
|
|
|
@ -42,7 +42,7 @@ module Mutations
|
|||
description: 'Squash commits on the source branch before merge.'
|
||||
|
||||
def resolve(project_path:, iid:, **args)
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42317')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/4796')
|
||||
|
||||
merge_request = authorized_find!(project_path: project_path, iid: iid)
|
||||
project = merge_request.target_project
|
||||
|
|
|
@ -52,12 +52,8 @@ class HipchatService < Service
|
|||
end
|
||||
|
||||
def execute(data)
|
||||
return unless supported_events.include?(data[:object_kind])
|
||||
|
||||
message = create_message(data)
|
||||
return unless message.present?
|
||||
|
||||
gate[room].send('GitLab', message, message_options(data)) # rubocop:disable GitlabSecurity/PublicSend
|
||||
# We removed the hipchat gem due to https://gitlab.com/gitlab-org/gitlab/-/issues/325851#note_537143149
|
||||
# HipChat is unusable anyway, so do nothing in this method
|
||||
end
|
||||
|
||||
def test(data)
|
||||
|
@ -72,71 +68,14 @@ class HipchatService < Service
|
|||
|
||||
private
|
||||
|
||||
def gate
|
||||
options = { api_version: api_version.presence || 'v2' }
|
||||
options[:server_url] = server unless server.blank?
|
||||
@gate ||= HipChat::Client.new(token, options)
|
||||
end
|
||||
|
||||
def message_options(data = nil)
|
||||
{ notify: notify.present? && Gitlab::Utils.to_boolean(notify), color: message_color(data) }
|
||||
end
|
||||
|
||||
def create_message(data)
|
||||
object_kind = data[:object_kind]
|
||||
|
||||
case object_kind
|
||||
when "push", "tag_push"
|
||||
create_push_message(data)
|
||||
when "issue"
|
||||
create_issue_message(data) unless update?(data)
|
||||
when "merge_request"
|
||||
create_merge_request_message(data) unless update?(data)
|
||||
when "note"
|
||||
create_note_message(data)
|
||||
when "pipeline"
|
||||
create_pipeline_message(data) if should_pipeline_be_notified?(data)
|
||||
end
|
||||
end
|
||||
|
||||
def render_line(text)
|
||||
markdown(text.lines.first.chomp, pipeline: :single_line) if text
|
||||
end
|
||||
|
||||
def create_push_message(push)
|
||||
ref_type = Gitlab::Git.tag_ref?(push[:ref]) ? 'tag' : 'branch'
|
||||
ref = Gitlab::Git.ref_name(push[:ref])
|
||||
|
||||
before = push[:before]
|
||||
after = push[:after]
|
||||
|
||||
message = []
|
||||
message << "#{push[:user_name]} "
|
||||
|
||||
if Gitlab::Git.blank_ref?(before)
|
||||
message << "pushed new #{ref_type} <a href=\""\
|
||||
"#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"\
|
||||
" to #{project_link}\n"
|
||||
elsif Gitlab::Git.blank_ref?(after)
|
||||
message << "removed #{ref_type} <b>#{ref}</b> from <a href=\"#{project.web_url}\">#{project_name}</a> \n"
|
||||
else
|
||||
message << "pushed to #{ref_type} <a href=\""\
|
||||
"#{project.web_url}/commits/#{CGI.escape(ref)}\">#{ref}</a> "
|
||||
message << "of <a href=\"#{project.web_url}\">#{project.full_name.gsub!(/\s/, '')}</a> "
|
||||
message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)"
|
||||
|
||||
push[:commits].take(MAX_COMMITS).each do |commit|
|
||||
message << "<br /> - #{render_line(commit[:message])} (<a href=\"#{commit[:url]}\">#{commit[:id][0..5]}</a>)"
|
||||
end
|
||||
|
||||
if push[:commits].count > MAX_COMMITS
|
||||
message << "<br />... #{push[:commits].count - MAX_COMMITS} more commits"
|
||||
end
|
||||
end
|
||||
|
||||
message.join
|
||||
end
|
||||
|
||||
def markdown(text, options = {})
|
||||
return "" unless text
|
||||
|
||||
|
@ -155,109 +94,10 @@ class HipchatService < Service
|
|||
sanitized_html.truncate(200, separator: ' ', omission: '...')
|
||||
end
|
||||
|
||||
def create_issue_message(data)
|
||||
user_name = data[:user][:name]
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
obj_attr = HashWithIndifferentAccess.new(obj_attr)
|
||||
title = render_line(obj_attr[:title])
|
||||
state = Issue.available_states.key(obj_attr[:state_id])
|
||||
issue_iid = obj_attr[:iid]
|
||||
issue_url = obj_attr[:url]
|
||||
description = obj_attr[:description]
|
||||
|
||||
issue_link = "<a href=\"#{issue_url}\">issue ##{issue_iid}</a>"
|
||||
|
||||
message = ["#{user_name} #{state} #{issue_link} in #{project_link}: <b>#{title}</b>"]
|
||||
message << "<pre>#{markdown(description)}</pre>"
|
||||
|
||||
message.join
|
||||
end
|
||||
|
||||
def create_merge_request_message(data)
|
||||
user_name = data[:user][:name]
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
obj_attr = HashWithIndifferentAccess.new(obj_attr)
|
||||
merge_request_id = obj_attr[:iid]
|
||||
state = obj_attr[:state]
|
||||
description = obj_attr[:description]
|
||||
title = render_line(obj_attr[:title])
|
||||
|
||||
merge_request_url = "#{project_url}/-/merge_requests/#{merge_request_id}"
|
||||
merge_request_link = "<a href=\"#{merge_request_url}\">merge request !#{merge_request_id}</a>"
|
||||
message = ["#{user_name} #{state} #{merge_request_link} in " \
|
||||
"#{project_link}: <b>#{title}</b>"]
|
||||
|
||||
message << "<pre>#{markdown(description)}</pre>"
|
||||
message.join
|
||||
end
|
||||
|
||||
def format_title(title)
|
||||
"<b>#{render_line(title)}</b>"
|
||||
end
|
||||
|
||||
def create_note_message(data)
|
||||
data = HashWithIndifferentAccess.new(data)
|
||||
user_name = data[:user][:name]
|
||||
|
||||
obj_attr = HashWithIndifferentAccess.new(data[:object_attributes])
|
||||
note = obj_attr[:note]
|
||||
note_url = obj_attr[:url]
|
||||
noteable_type = obj_attr[:noteable_type]
|
||||
commit_id = nil
|
||||
|
||||
case noteable_type
|
||||
when "Commit"
|
||||
commit_attr = HashWithIndifferentAccess.new(data[:commit])
|
||||
commit_id = commit_attr[:id]
|
||||
subject_desc = commit_id
|
||||
subject_desc = Commit.truncate_sha(subject_desc)
|
||||
subject_type = "commit"
|
||||
title = format_title(commit_attr[:message])
|
||||
when "Issue"
|
||||
subj_attr = HashWithIndifferentAccess.new(data[:issue])
|
||||
subject_id = subj_attr[:iid]
|
||||
subject_desc = "##{subject_id}"
|
||||
subject_type = "issue"
|
||||
title = format_title(subj_attr[:title])
|
||||
when "MergeRequest"
|
||||
subj_attr = HashWithIndifferentAccess.new(data[:merge_request])
|
||||
subject_id = subj_attr[:iid]
|
||||
subject_desc = "!#{subject_id}"
|
||||
subject_type = "merge request"
|
||||
title = format_title(subj_attr[:title])
|
||||
when "Snippet"
|
||||
subj_attr = HashWithIndifferentAccess.new(data[:snippet])
|
||||
subject_id = subj_attr[:id]
|
||||
subject_desc = "##{subject_id}"
|
||||
subject_type = "snippet"
|
||||
title = format_title(subj_attr[:title])
|
||||
end
|
||||
|
||||
subject_html = "<a href=\"#{note_url}\">#{subject_type} #{subject_desc}</a>"
|
||||
message = ["#{user_name} commented on #{subject_html} in #{project_link}: "]
|
||||
message << title
|
||||
|
||||
message << "<pre>#{markdown(note, ref: commit_id)}</pre>"
|
||||
message.join
|
||||
end
|
||||
|
||||
def create_pipeline_message(data)
|
||||
pipeline_attributes = data[:object_attributes]
|
||||
pipeline_id = pipeline_attributes[:id]
|
||||
ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
|
||||
ref = pipeline_attributes[:ref]
|
||||
user_name = (data[:user] && data[:user][:name]) || 'API'
|
||||
status = pipeline_attributes[:status]
|
||||
duration = pipeline_attributes[:duration]
|
||||
|
||||
branch_link = "<a href=\"#{project_url}/-/commits/#{CGI.escape(ref)}\">#{ref}</a>"
|
||||
pipeline_url = "<a href=\"#{project_url}/-/pipelines/#{pipeline_id}\">##{pipeline_id}</a>"
|
||||
|
||||
"#{project_link}: Pipeline #{pipeline_url} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)"
|
||||
end
|
||||
|
||||
def message_color(data)
|
||||
pipeline_status_color(data) || color || 'yellow'
|
||||
end
|
||||
|
@ -309,5 +149,3 @@ class HipchatService < Service
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
HipchatService.prepend_if_ee('EE::HipchatService')
|
||||
|
|
|
@ -48,7 +48,7 @@ class PostReceiveService
|
|||
end
|
||||
|
||||
def process_mr_push_options(push_options, changes)
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/61359')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/28494')
|
||||
return unless repository
|
||||
|
||||
unless repository.repo_type.project?
|
||||
|
|
|
@ -1,79 +1,6 @@
|
|||
- form_classes = %w[wiki-form common-note-form gl-mt-3 js-quick-submit]
|
||||
- page_info = { last_commit_sha: @page.last_commit_sha, persisted: @page.persisted?, title: @page.title, content: @page.content || '', format: @page.format.to_s, uploads_path: uploads_path, path: wiki_page_path(@wiki, @page), wiki_path: wiki_path(@wiki), help_path: help_page_path('user/project/wiki/index'), markdown_help_path: help_page_path('user/markdown'), markdown_preview_path: wiki_page_path(@wiki, @page, action: :preview_markdown), create_path: wiki_path(@wiki, action: :create) }
|
||||
|
||||
- if @page.persisted?
|
||||
- form_action = wiki_page_path(@wiki, @page)
|
||||
- form_method = :put
|
||||
- else
|
||||
- form_action = wiki_path(@wiki, action: :create)
|
||||
- form_method = :post
|
||||
- form_classes << 'js-new-wiki-page'
|
||||
|
||||
= form_for @page, url: form_action, method: form_method,
|
||||
html: { class: form_classes },
|
||||
data: { uploads_path: uploads_path } do |f|
|
||||
.gl-mt-3
|
||||
= form_errors(@page, truncate: :title)
|
||||
|
||||
- if @page.persisted?
|
||||
= f.hidden_field :last_commit_sha, value: @page.last_commit_sha
|
||||
|
||||
.form-group.row
|
||||
.col-sm-2.col-form-label= f.label :title, class: 'control-label-full-width'
|
||||
.col-sm-10
|
||||
= f.text_field :title, class: 'form-control qa-wiki-title-textbox', value: @page.title, required: true, autofocus: !@page.persisted?, placeholder: s_('Wiki|Page title')
|
||||
%span.gl-display-inline-block.gl-max-w-full.gl-mt-2.gl-text-gray-600
|
||||
= sprite_icon('bulb', size: 12, css_class: 'gl-mr-n1')
|
||||
- if @page.persisted?
|
||||
= s_("WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title.")
|
||||
= link_to sprite_icon('question-o'), help_page_path('user/project/wiki/index', anchor: 'moving-a-wiki-page'),
|
||||
target: '_blank', rel: 'noopener noreferrer'
|
||||
- else
|
||||
= s_("WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories.")
|
||||
= succeed '.' do
|
||||
= link_to _('More information'), help_page_path('user/project/wiki/index', anchor: 'creating-a-new-wiki-page'),
|
||||
target: '_blank', rel: 'noopener noreferrer'
|
||||
.form-group.row
|
||||
.col-sm-2.col-form-label= f.label :format, class: 'control-label-full-width'
|
||||
.col-sm-10
|
||||
.select-wrapper
|
||||
= f.select :format, options_for_select(Wiki::MARKUPS, {selected: @page.format}), {}, class: 'form-control select-control'
|
||||
= sprite_icon('chevron-down', css_class: 'gl-absolute gl-top-3 gl-right-3 gl-text-gray-200')
|
||||
|
||||
.form-group.row
|
||||
.col-sm-2.col-form-label= f.label :content, class: 'control-label-full-width'
|
||||
.col-sm-10
|
||||
= render layout: 'shared/md_preview', locals: { url: wiki_page_path(@wiki, @page, action: :preview_markdown) } do
|
||||
= render 'shared/zen', f: f, attr: :content, classes: 'note-textarea qa-wiki-content-textarea', placeholder: s_("WikiPage|Write your content or drag files here…"), autofocus: @page.persisted?
|
||||
= render 'shared/notes/hints'
|
||||
|
||||
.clearfix
|
||||
.error-alert
|
||||
|
||||
.form-text.gl-text-gray-600
|
||||
= succeed '.' do
|
||||
- case @page.format.to_s
|
||||
- when 'rdoc'
|
||||
- link_example = '{Link title}[link:page-slug]'
|
||||
- when 'asciidoc'
|
||||
- link_example = 'link:page-slug[Link title]'
|
||||
- when 'org'
|
||||
- link_example = '[[page-slug]]'
|
||||
- else
|
||||
- link_example = '[Link Title](page-slug)'
|
||||
= html_escape(s_('WikiMarkdownTip|To link to a (new) page, simply type %{link_example}')) % { link_example: tag.code(link_example, class: 'js-markup-link-example') }
|
||||
= succeed '.' do
|
||||
- markdown_link = link_to s_("WikiMarkdownDocs|documentation"), help_page_path('user/markdown', anchor: 'wiki-specific-markdown')
|
||||
= (s_("WikiMarkdownDocs|More examples are in the %{docs_link}") % { docs_link: markdown_link }).html_safe
|
||||
|
||||
.form-group.row
|
||||
.col-sm-2.col-form-label= f.label :commit_message, class: 'control-label-full-width'
|
||||
.col-sm-10= f.text_field :message, class: 'form-control qa-wiki-message-textbox', rows: 18, value: nil
|
||||
|
||||
.form-actions
|
||||
- if @page && @page.persisted?
|
||||
= f.submit _("Save changes"), class: 'btn gl-button btn-confirm qa-save-changes-button js-wiki-btn-submit', disabled: 'true'
|
||||
.float-right
|
||||
= link_to _("Cancel"), wiki_page_path(@wiki, @page), class: 'btn gl-button btn-cancel btn-default'
|
||||
- else
|
||||
= f.submit s_("Wiki|Create page"), class: 'btn-confirm gl-button btn qa-create-page-button rspec-create-page-button js-wiki-btn-submit', disabled: 'true'
|
||||
.float-right
|
||||
= link_to _("Cancel"), wiki_path(@wiki), class: 'btn gl-button btn-cancel btn-default'
|
||||
#js-wiki-form{ data: { page_info: page_info.to_json, format_options: Wiki::MARKUPS.to_json } }
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add message for repository backup skip
|
||||
merge_request: 54285
|
||||
author:
|
||||
type: other
|
5
changelogs/unreleased/remove_hipchat_gem.yml
Normal file
5
changelogs/unreleased/remove_hipchat_gem.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Make HipChat project service do nothing
|
||||
merge_request: 57434
|
||||
author:
|
||||
type: removed
|
5
changelogs/unreleased/update-contribution-tooltip.yml
Normal file
5
changelogs/unreleased/update-contribution-tooltip.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add multi-line styling within contribution tooltip
|
||||
merge_request: 54765
|
||||
author: Yogi (@yo)
|
||||
type: changed
|
|
@ -1,15 +0,0 @@
|
|||
# frozen_string_literal: true
|
||||
# This monkey patches the HTTParty used in https://github.com/hipchat/hipchat-rb.
|
||||
module HipChat
|
||||
class Client
|
||||
connection_adapter ::Gitlab::HTTPConnectionAdapter
|
||||
end
|
||||
|
||||
class Room
|
||||
connection_adapter ::Gitlab::HTTPConnectionAdapter
|
||||
end
|
||||
|
||||
class User
|
||||
connection_adapter ::Gitlab::HTTPConnectionAdapter
|
||||
end
|
||||
end
|
|
@ -89,7 +89,7 @@ _The artifacts are stored by default in
|
|||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/1762) in
|
||||
> [GitLab Premium](https://about.gitlab.com/pricing/) 9.4.
|
||||
> - Since version 9.5, artifacts are [browsable](../ci/pipelines/job_artifacts.md#browsing-artifacts),
|
||||
> - Since version 9.5, artifacts are [browsable](../ci/pipelines/job_artifacts.md#download-job-artifacts),
|
||||
> when object storage is enabled. 9.4 lacks this feature.
|
||||
> - Since version 10.6, available in [GitLab Free](https://about.gitlab.com/pricing/).
|
||||
> - Since version 11.0, we support `direct_upload` to S3.
|
||||
|
@ -509,7 +509,7 @@ If you need to manually remove job artifacts associated with multiple jobs while
|
|||
|
||||
NOTE:
|
||||
This step also erases artifacts that users have chosen to
|
||||
["keep"](../ci/pipelines/job_artifacts.md#browsing-artifacts).
|
||||
["keep"](../ci/pipelines/job_artifacts.md#download-job-artifacts).
|
||||
|
||||
```ruby
|
||||
builds_to_clear = builds_with_artifacts.where("finished_at < ?", 1.week.ago)
|
||||
|
@ -583,3 +583,21 @@ If you need to manually remove **all** job artifacts associated with multiple jo
|
|||
- `7.days.ago`
|
||||
- `3.months.ago`
|
||||
- `1.year.ago`
|
||||
|
||||
### Error `Downloading artifacts from coordinator... not found`
|
||||
|
||||
When a job tries to download artifacts from an earlier job, you might receive an error similar to:
|
||||
|
||||
```plaintext
|
||||
Downloading artifacts from coordinator... not found id=12345678 responseStatus=404 Not Found
|
||||
```
|
||||
|
||||
This might be caused by a `gitlab.rb` file with the following configuration:
|
||||
|
||||
```ruby
|
||||
gitlab_rails['artifacts_object_store_background_upload'] = false
|
||||
gitlab_rails['artifacts_object_store_direct_upload'] = true
|
||||
```
|
||||
|
||||
To prevent this, comment out or remove those lines, or switch to their [default values](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template),
|
||||
then run `sudo gitlab-ctl reconfigure`.
|
||||
|
|
|
@ -487,7 +487,7 @@ internet connectivity is gated by a proxy. To use a proxy for GitLab Pages:
|
|||
### Using a custom Certificate Authority (CA)
|
||||
|
||||
When using certificates issued by a custom CA, [Access Control](../../user/project/pages/pages_access_control.md#gitlab-pages-access-control) and
|
||||
the [online view of HTML job artifacts](../../ci/pipelines/job_artifacts.md#browsing-artifacts)
|
||||
the [online view of HTML job artifacts](../../ci/pipelines/job_artifacts.md#download-job-artifacts)
|
||||
fails to work if the custom CA is not recognized.
|
||||
|
||||
This usually results in this error:
|
||||
|
|
|
@ -8,25 +8,19 @@ type: reference, howto
|
|||
|
||||
# Job artifacts
|
||||
|
||||
> - Introduced in GitLab 8.2 and GitLab Runner 0.7.0.
|
||||
> - Starting with GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format changed to `ZIP`, and it's now possible to browse its contents, with the added ability of downloading the files separately.
|
||||
> - In GitLab 8.17, builds were renamed to jobs.
|
||||
> - The artifacts browser is available only for new artifacts that are sent to GitLab using GitLab Runner version 1.0 and up. You cannot browse old artifacts already uploaded to GitLab.
|
||||
> Introduced in [GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16675), artifacts in internal and private projects can be previewed when [GitLab Pages access control](../../administration/pages/index.md#access-control) is enabled.
|
||||
|
||||
Job artifacts are a list of files and directories created by a job
|
||||
once it finishes. This feature is [enabled by default](../../administration/job_artifacts.md) in all
|
||||
GitLab installations.
|
||||
Jobs can output an archive of files and directories. This output is known as a job artifact.
|
||||
|
||||
Job artifacts created by GitLab Runner are uploaded to GitLab and are downloadable as a single archive using the GitLab UI or the [GitLab API](../../api/job_artifacts.md#get-job-artifacts).
|
||||
You can download job artifacts by using the GitLab UI or the [API](../../api/job_artifacts.md#get-job-artifacts).
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an overview, watch the video [GitLab CI Pipeline, Artifacts, and Environments](https://www.youtube.com/watch?v=PCKDICEe10s).
|
||||
Watch also [GitLab CI pipeline tutorial for beginners](https://www.youtube.com/watch?v=Jav4vbUrqII).
|
||||
|
||||
## Defining artifacts in `.gitlab-ci.yml`
|
||||
## Define artifacts in the `.gitlab-ci.yml` file
|
||||
|
||||
A simple example of using the artifacts definition in `.gitlab-ci.yml` would be
|
||||
the following:
|
||||
This example shows how to configure your `.gitlab-ci.yml` file to create job artifacts:
|
||||
|
||||
```yaml
|
||||
pdf:
|
||||
|
@ -38,19 +32,169 @@ pdf:
|
|||
```
|
||||
|
||||
A job named `pdf` calls the `xelatex` command to build a PDF file from the
|
||||
latex source file `mycv.tex`. We then define the `artifacts` paths which in
|
||||
turn are defined with the `paths` keyword. All paths to files and directories
|
||||
are relative to the repository that was cloned during the build.
|
||||
LaTeX source file, `mycv.tex`.
|
||||
|
||||
By default, the artifacts upload when the job succeeds. You can also set artifacts to upload
|
||||
when the job fails, or always, by using [`artifacts:when`](../yaml/README.md#artifactswhen)
|
||||
keyword. GitLab keeps these uploaded artifacts for 1 week, as defined
|
||||
by the `expire_in` definition. You can keep the artifacts from expiring
|
||||
via the [web interface](#browsing-artifacts). If the expiry time is not defined, it defaults
|
||||
to the [instance wide setting](../../user/admin_area/settings/continuous_integration.md#default-artifacts-expiration).
|
||||
The `paths` keyword determines which files to add to the job artifacts.
|
||||
All paths to files and directories are relative to the repository where the job was created.
|
||||
|
||||
For more examples on artifacts, follow the [artifacts reference in
|
||||
`.gitlab-ci.yml`](../yaml/README.md#artifacts).
|
||||
The `expire_in` keyword determines how long GitLab keeps the job artifacts.
|
||||
You can also [use the UI to keep job artifacts from expiring](#download-job-artifacts).
|
||||
If `expire_in` is not defined, the
|
||||
[instance-wide setting](../../user/admin_area/settings/continuous_integration.md#default-artifacts-expiration)
|
||||
is used.
|
||||
|
||||
For more examples, view the [keyword reference for the `.gitlab-ci.yml` file](../yaml/README.md#artifacts).
|
||||
|
||||
## Download job artifacts
|
||||
|
||||
You can download job artifacts or view the job archive:
|
||||
|
||||
- On the **Pipelines** page, to the right of the pipeline:
|
||||
|
||||
![Job artifacts in Pipelines page](img/job_artifacts_pipelines_page.png)
|
||||
|
||||
- On the **Jobs** page, to the right of the job:
|
||||
|
||||
![Job artifacts in Builds page](img/job_artifacts_builds_page.png)
|
||||
|
||||
- On a job's detail page. The **Keep** button indicates an `expire_in` value was set:
|
||||
|
||||
![Job artifacts browser button](img/job_artifacts_browser_button.png)
|
||||
|
||||
- On a merge request, by the pipeline details:
|
||||
|
||||
![Job artifacts in Merge Request](img/job_artifacts_merge_request.png)
|
||||
|
||||
- When browsing an archive:
|
||||
|
||||
![Job artifacts browser](img/job_artifacts_browser.png)
|
||||
|
||||
If [GitLab Pages](../../administration/pages/index.md) is enabled in the project, you can preview
|
||||
HTML files in the artifacts directly in your browser. If the project is internal or private, you must
|
||||
enable [GitLab Pages access control](../../administration/pages/index.md#access-control) to preview
|
||||
HTML files.
|
||||
|
||||
## View failed job artifacts
|
||||
|
||||
If the latest job has failed to upload the artifacts, you can see that
|
||||
information in the UI.
|
||||
|
||||
![Latest artifacts button](img/job_latest_artifacts_browser.png)
|
||||
|
||||
## Erase job artifacts
|
||||
|
||||
WARNING:
|
||||
This is a destructive action that leads to data loss. Use with caution.
|
||||
|
||||
You can erase a single job, which also removes the job's
|
||||
artifacts and log. You must be:
|
||||
|
||||
- The owner of the job.
|
||||
- A [Maintainer](../../user/permissions.md#gitlab-cicd-permissions) of the project.
|
||||
|
||||
To erase a job:
|
||||
|
||||
1. Go to a job's detail page.
|
||||
1. At the top right of the job's log, select the trash icon.
|
||||
1. Confirm the deletion.
|
||||
|
||||
## Use GitLab CI/CD to retrieve job artifacts for private projects
|
||||
|
||||
To retrieve a job artifact from a different project, you might need to use a
|
||||
private token to [authenticate and download](../../api/job_artifacts.md#get-job-artifacts)
|
||||
the artifact.
|
||||
|
||||
## The latest job artifacts
|
||||
|
||||
Job artifacts created in the most recent successful pipeline for a specific ref
|
||||
are considered the latest artifacts. If you run two types of pipelines for the same ref,
|
||||
timing determines which artifacts are the latest.
|
||||
|
||||
For example, if a merge request creates a branch pipeline at the same time as
|
||||
a scheduled pipeline, the pipeline that finished most recently creates the latest job artifact.
|
||||
|
||||
In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/201784) and later, artifacts
|
||||
for [parent and child pipelines](../parent_child_pipelines.md) are searched in hierarchical
|
||||
order from parent to child. For example, if both parent and child pipelines have a
|
||||
job with the same name, the job artifact from the parent pipeline is returned.
|
||||
|
||||
## Access the latest job artifacts by URL
|
||||
|
||||
You can download the latest job artifacts by using a URL.
|
||||
|
||||
To download the whole artifacts archive:
|
||||
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/download?job=<job_name>
|
||||
```
|
||||
|
||||
To download a single file from the artifacts:
|
||||
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/raw/<path_to_file>?job=<job_name>
|
||||
```
|
||||
|
||||
For example, to download the latest artifacts of the job named `coverage` of
|
||||
the `master` branch of the `gitlab` project that belongs to the `gitlab-org`
|
||||
namespace:
|
||||
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/download?job=coverage
|
||||
```
|
||||
|
||||
To download the file `coverage/index.html` from the same artifacts:
|
||||
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/raw/coverage/index.html?job=coverage
|
||||
```
|
||||
|
||||
To browse the latest job artifacts:
|
||||
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/browse?job=<job_name>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/browse?job=coverage
|
||||
```
|
||||
|
||||
There is also a URL for specific files, including HTML files that
|
||||
are shown in [GitLab Pages](../../administration/pages/index.md):
|
||||
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/file/<path>?job=<job_name>
|
||||
```
|
||||
|
||||
For example, when a job `coverage` creates the artifact `htmlcov/index.html`:
|
||||
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/file/htmlcov/index.html?job=coverage
|
||||
```
|
||||
|
||||
### Keep artifacts from most recent successful jobs
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16267) in GitLab 13.0.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/229936) in GitLab 13.4.
|
||||
> - [Made optional with a CI/CD setting](https://gitlab.com/gitlab-org/gitlab/-/issues/241026) in GitLab 13.8.
|
||||
|
||||
By default, the latest job artifacts from the most recent successful jobs are never deleted.
|
||||
If a job is configured with [`expire_in`](../yaml/README.md#artifactsexpire_in),
|
||||
its artifacts only expire if a more recent artifact exists.
|
||||
|
||||
Keeping the latest artifacts can use a large amount of storage space in projects
|
||||
with a lot of jobs or large artifacts. If the latest artifacts are not needed in
|
||||
a project, you can disable this behavior to save space:
|
||||
|
||||
1. Go to the project's **Settings > CI/CD > Artifacts**.
|
||||
1. Clear the **Keep artifacts from most recent successful jobs** checkbox.
|
||||
|
||||
You can disable this behavior for all projects on a self-managed instance in the
|
||||
[instance's CI/CD settings](../../user/admin_area/settings/continuous_integration.md#keep-the-latest-artifacts-for-all-jobs-in-the-latest-successful-pipelines).
|
||||
|
||||
When you disable the feature, the latest artifacts do not immediately expire.
|
||||
A new pipeline must run before the latest artifacts can expire and be deleted.
|
||||
|
||||
### `artifacts:reports`
|
||||
|
||||
|
@ -306,191 +450,7 @@ The collected Requirements report uploads to GitLab as an artifact and
|
|||
existing [requirements](../../user/project/requirements/index.md) are
|
||||
marked as Satisfied.
|
||||
|
||||
## Browsing artifacts
|
||||
|
||||
> - From GitLab 9.2, PDFs, images, videos, and other formats can be previewed directly in the job artifacts browser without the need to download them.
|
||||
> - Introduced in [GitLab 10.1](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/14399), HTML files in a public project can be previewed directly in a new tab without the need to download them when [GitLab Pages](../../administration/pages/index.md) is enabled. The same applies for textual formats (currently supported extensions: `.txt`, `.json`, and `.log`).
|
||||
> - Introduced in [GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/16675), artifacts in internal and private projects can be previewed when [GitLab Pages access control](../../administration/pages/index.md#access-control) is enabled.
|
||||
|
||||
After a job finishes, if you visit the job's specific page, there are three
|
||||
buttons. You can download the artifacts archive or browse its contents, whereas
|
||||
the **Keep** button appears only if you've set an [expiry date](../yaml/README.md#artifactsexpire_in) to the
|
||||
artifacts in case you changed your mind and want to keep them.
|
||||
|
||||
![Job artifacts browser button](img/job_artifacts_browser_button.png)
|
||||
|
||||
The archive browser shows the name and the actual file size of each file in the
|
||||
archive. If your artifacts contained directories, then you're also able to
|
||||
browse inside them.
|
||||
|
||||
Below you can see what browsing looks like. In this case we have browsed inside
|
||||
the archive and at this point there is one directory, a couple files, and
|
||||
one HTML file that you can view directly online when
|
||||
[GitLab Pages](../../administration/pages/index.md) is enabled (opens in a new tab).
|
||||
Select artifacts in internal and private projects can only be previewed when
|
||||
[GitLab Pages access control](../../administration/pages/index.md#access-control) is enabled.
|
||||
|
||||
![Job artifacts browser](img/job_artifacts_browser.png)
|
||||
|
||||
## Downloading artifacts
|
||||
|
||||
If you need to download an artifact or the whole archive, there are buttons in various places
|
||||
in the GitLab UI to do this:
|
||||
|
||||
1. While on the pipelines page, you can see the download icon for each job's
|
||||
artifacts and archive in the right corner:
|
||||
|
||||
![Job artifacts in Pipelines page](img/job_artifacts_pipelines_page.png)
|
||||
|
||||
1. While on the **Jobs** page, you can see the download icon for each job's
|
||||
artifacts and archive in the right corner:
|
||||
|
||||
![Job artifacts in Builds page](img/job_artifacts_builds_page.png)
|
||||
|
||||
1. While inside a specific job, you're presented with a download button
|
||||
along with the one that browses the archive:
|
||||
|
||||
![Job artifacts browser button](img/job_artifacts_browser_button.png)
|
||||
|
||||
1. While on the details page of a merge request, you can see the download
|
||||
icon for each job's artifacts on the right side of the merge request widget:
|
||||
|
||||
![Job artifacts in Merge Request](img/job_artifacts_merge_request.png)
|
||||
|
||||
1. And finally, when browsing an archive you can see the download button at
|
||||
the top right corner:
|
||||
|
||||
![Job artifacts browser](img/job_artifacts_browser.png)
|
||||
|
||||
## Downloading the latest artifacts
|
||||
|
||||
It's possible to download the latest artifacts of a job via a well known URL
|
||||
so you can use it for scripting purposes.
|
||||
|
||||
NOTE:
|
||||
The latest artifacts are created by jobs in the **most recent** successful pipeline
|
||||
for the specific ref. If you run two types of pipelines for the same ref, timing determines the latest
|
||||
artifact. For example, if a merge request creates a branch pipeline at the same time as a scheduled pipeline, the pipeline that completed most recently creates the latest artifact.
|
||||
|
||||
In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/201784) and later, artifacts
|
||||
for [parent and child pipelines](../parent_child_pipelines.md) are searched in hierarchical
|
||||
order from parent to child. For example, if both parent and child pipelines have a
|
||||
job with the same name, the artifact from the parent pipeline is returned.
|
||||
|
||||
Artifacts for other pipelines can be accessed with direct access to them.
|
||||
|
||||
The structure of the URL to download the whole artifacts archive is the following:
|
||||
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/download?job=<job_name>
|
||||
```
|
||||
|
||||
To download a single file from the artifacts use the following URL:
|
||||
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/raw/<path_to_file>?job=<job_name>
|
||||
```
|
||||
|
||||
For example, to download the latest artifacts of the job named `coverage` of
|
||||
the `master` branch of the `gitlab` project that belongs to the `gitlab-org`
|
||||
namespace, the URL would be:
|
||||
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/download?job=coverage
|
||||
```
|
||||
|
||||
To download the file `coverage/index.html` from the same
|
||||
artifacts use the following URL:
|
||||
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/raw/coverage/index.html?job=coverage
|
||||
```
|
||||
|
||||
There is also a URL to browse the latest job artifacts:
|
||||
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/browse?job=<job_name>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/browse?job=coverage
|
||||
```
|
||||
|
||||
There is also a URL to specific files, including HTML files that
|
||||
are shown in [GitLab Pages](../../administration/pages/index.md):
|
||||
|
||||
```plaintext
|
||||
https://example.com/<namespace>/<project>/-/jobs/artifacts/<ref>/file/<path>?job=<job_name>
|
||||
```
|
||||
|
||||
For example, when a job `coverage` creates the artifact `htmlcov/index.html`,
|
||||
you can access it at:
|
||||
|
||||
```plaintext
|
||||
https://gitlab.com/gitlab-org/gitlab/-/jobs/artifacts/master/file/htmlcov/index.html?job=coverage
|
||||
```
|
||||
|
||||
The latest builds are also exposed in the UI in various places. Specifically,
|
||||
look for the download button in:
|
||||
|
||||
- The main project's page
|
||||
- The branches page
|
||||
- The tags page
|
||||
|
||||
If the latest job has failed to upload the artifacts, you can see that
|
||||
information in the UI.
|
||||
|
||||
![Latest artifacts button](img/job_latest_artifacts_browser.png)
|
||||
|
||||
## Erasing artifacts
|
||||
|
||||
WARNING:
|
||||
This is a destructive action that leads to data loss. Use with caution.
|
||||
|
||||
You can erase a single job via the UI, which also removes the job's
|
||||
artifacts and trace, if you are:
|
||||
|
||||
- The owner of the job.
|
||||
- A [Maintainer](../../user/permissions.md#gitlab-cicd-permissions) of the project.
|
||||
|
||||
To erase a job:
|
||||
|
||||
1. Navigate to a job's page.
|
||||
1. Click the trash icon at the top right of the job's trace.
|
||||
1. Confirm the deletion.
|
||||
|
||||
## Retrieve artifacts of private projects when using GitLab CI
|
||||
|
||||
To retrieve a job artifact from a different project, you might need to use a
|
||||
private token to [authenticate and download](../../api/job_artifacts.md#get-job-artifacts)
|
||||
the artifact.
|
||||
|
||||
## Keep artifacts from most recent successful jobs
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/16267) in GitLab 13.0.
|
||||
> - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/229936) in GitLab 13.4.
|
||||
> - [Made optional with a CI/CD setting](https://gitlab.com/gitlab-org/gitlab/-/issues/241026) in GitLab 13.8.
|
||||
|
||||
By default, the latest artifacts from the most recent successful jobs are never deleted.
|
||||
If a job is configured with [`expire_in`](../yaml/README.md#artifactsexpire_in),
|
||||
its artifacts only expire if a more recent artifact exists.
|
||||
|
||||
Keeping the latest artifacts can use a large amount of storage space in projects
|
||||
with a lot of jobs or large artifacts. If the latest artifacts are not needed in
|
||||
a project, you can disable this behavior to save space:
|
||||
|
||||
1. Navigate to **Settings > CI/CD > Artifacts**.
|
||||
1. Uncheck **Keep artifacts from most recent successful jobs**.
|
||||
|
||||
You can disable this behavior for all projects on a self-managed instance in the
|
||||
[instance's CI/CD settings](../../user/admin_area/settings/continuous_integration.md#keep-the-latest-artifacts-for-all-jobs-in-the-latest-successful-pipelines). **(CORE ONLY)**
|
||||
|
||||
When you disable the feature, the latest artifacts do not immediately expire.
|
||||
A new pipeline must run before the latest artifacts can expire and be deleted.
|
||||
|
||||
## Troubleshooting
|
||||
## Troubleshooting job artifacts
|
||||
|
||||
### Error message `No files to upload`
|
||||
|
||||
|
|
|
@ -3239,7 +3239,7 @@ Note the following:
|
|||
- Artifacts do not display in the merge request UI when using variables to define the `artifacts:paths`.
|
||||
- A maximum of 10 job artifacts per merge request can be exposed.
|
||||
- Glob patterns are unsupported.
|
||||
- If a directory is specified, the link is to the job [artifacts browser](../pipelines/job_artifacts.md#browsing-artifacts) if there is more than
|
||||
- If a directory is specified, the link is to the job [artifacts browser](../pipelines/job_artifacts.md#download-job-artifacts) if there is more than
|
||||
one file in the directory.
|
||||
- For exposed single file artifacts with `.html`, `.htm`, `.txt`, `.json`, `.xml`,
|
||||
and `.log` extensions, if [GitLab Pages](../../administration/pages/index.md) is:
|
||||
|
@ -3508,7 +3508,7 @@ deploy:
|
|||
|
||||
If the artifacts of the job that is set as a dependency are
|
||||
[expired](#artifactsexpire_in) or
|
||||
[erased](../pipelines/job_artifacts.md#erasing-artifacts), then
|
||||
[erased](../pipelines/job_artifacts.md#erase-job-artifacts), then
|
||||
the dependent job fails.
|
||||
|
||||
You can ask your administrator to
|
||||
|
|
|
@ -141,7 +141,7 @@ The `gitlab-cov-fuzz` tool emits a JSON report file. For more information, see t
|
|||
[schema for this report](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/coverage-fuzzing-report-format.json).
|
||||
|
||||
You can download the JSON report file from the CI pipelines page. For more information, see
|
||||
[Downloading artifacts](../../../ci/pipelines/job_artifacts.md#downloading-artifacts).
|
||||
[Downloading artifacts](../../../ci/pipelines/job_artifacts.md#download-job-artifacts).
|
||||
|
||||
Here's an example coverage fuzzing report:
|
||||
|
||||
|
|
|
@ -536,7 +536,7 @@ The SAST tool emits a JSON report file. For more information, see the
|
|||
[schema for this report](https://gitlab.com/gitlab-org/security-products/security-report-schemas/-/blob/master/dist/sast-report-format.json).
|
||||
|
||||
The JSON report file can be downloaded from the CI pipelines page, or the
|
||||
pipelines tab on merge requests by [setting `artifacts: paths`](../../../ci/pipelines/job_artifacts.md#defining-artifacts-in-gitlab-ciyml) to `gl-sast-report.json`. For more information see [Downloading artifacts](../../../ci/pipelines/job_artifacts.md).
|
||||
pipelines tab on merge requests by [setting `artifacts: paths`](../../../ci/yaml/README.md#artifactspaths) to `gl-sast-report.json`. For more information see [Downloading artifacts](../../../ci/pipelines/job_artifacts.md).
|
||||
|
||||
Here's an example SAST report:
|
||||
|
||||
|
|
|
@ -4,7 +4,12 @@ group: Ecosystem
|
|||
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
|
||||
---
|
||||
|
||||
# Atlassian HipChat **(FREE)**
|
||||
# Atlassian HipChat (Deprecated) **(FREE)**
|
||||
|
||||
As of [GitLab
|
||||
13.11](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57434), the
|
||||
HipChat integration will not send any notifications to HipChat. This
|
||||
integration is also deprecated.
|
||||
|
||||
GitLab provides a way to send HipChat notifications upon a number of events,
|
||||
such as when a user pushes code, creates a branch or tag, adds a comment, and
|
||||
|
|
|
@ -57,7 +57,7 @@ include:
|
|||
creates an `a11y` job in your CI/CD pipeline, runs
|
||||
Pa11y against the web pages defined in `a11y_urls`, and builds an HTML report for each.
|
||||
|
||||
The report for each URL is saved as an artifact that can be [viewed directly in your browser](../../../ci/pipelines/job_artifacts.md#browsing-artifacts).
|
||||
The report for each URL is saved as an artifact that can be [viewed directly in your browser](../../../ci/pipelines/job_artifacts.md#download-job-artifacts).
|
||||
|
||||
A single `gl-accessibility.json` artifact is created and saved along with the individual HTML reports.
|
||||
It includes report data for all URLs scanned.
|
||||
|
|
|
@ -355,7 +355,7 @@ After the Code Quality job completes:
|
|||
The Code Quality widget in the merge request compares the reports from the base and head of the branch,
|
||||
then lists any violations that are resolved or created when the branch is merged.
|
||||
- The full JSON report is available as a
|
||||
[downloadable artifact](../../../ci/pipelines/job_artifacts.md#downloading-artifacts)
|
||||
[downloadable artifact](../../../ci/pipelines/job_artifacts.md#download-job-artifacts)
|
||||
for the `code_quality` job.
|
||||
- The full list of code quality violations generated by a pipeline is shown in the
|
||||
Code Quality tab of the Pipeline Details page. **(PREMIUM)**
|
||||
|
|
|
@ -290,7 +290,7 @@ This problem most likely results from a missing `index.html` file in the public
|
|||
a 404 is encountered, confirm that the public directory contains an `index.html` file. If the file contains a different name
|
||||
such as `test.html`, the Pages site can still be accessed, but the full path would be needed. For example: `https//group-name.pages.example.com/project-name/test.html`.
|
||||
|
||||
The contents of the public directory can be confirmed by [browsing the artifacts](../../../ci/pipelines/job_artifacts.md#browsing-artifacts) from the latest pipeline.
|
||||
The contents of the public directory can be confirmed by [browsing the artifacts](../../../ci/pipelines/job_artifacts.md#download-job-artifacts) from the latest pipeline.
|
||||
|
||||
Files listed under the public directory can be accessed through the Pages URL for the project.
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ module API
|
|||
optional :variables, Array, desc: 'Array of variables available in the pipeline'
|
||||
end
|
||||
post ':id/pipeline' do
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42124')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20711')
|
||||
|
||||
authorize! :create_pipeline, user_project
|
||||
|
||||
|
|
|
@ -112,7 +112,7 @@ module API
|
|||
end
|
||||
|
||||
def delete_group(group)
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/46285')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/22226')
|
||||
destroy_conditionally!(group) do |group|
|
||||
::Groups::DestroyService.new(group, current_user).async_execute
|
||||
end
|
||||
|
|
|
@ -242,7 +242,7 @@ module API
|
|||
use :issue_params
|
||||
end
|
||||
post ':id/issues' do
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42320')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20773')
|
||||
|
||||
check_rate_limit! :issues_create, [current_user]
|
||||
|
||||
|
@ -288,7 +288,7 @@ module API
|
|||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
put ':id/issues/:issue_iid' do
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42322')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20775')
|
||||
|
||||
issue = user_project.issues.find_by!(iid: params.delete(:issue_iid))
|
||||
authorize! :update_issue, issue
|
||||
|
@ -346,7 +346,7 @@ module API
|
|||
end
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
post ':id/issues/:issue_iid/move' do
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42323')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20776')
|
||||
|
||||
issue = user_project.issues.find_by(iid: params[:issue_iid])
|
||||
not_found!('Issue') unless issue
|
||||
|
|
|
@ -207,7 +207,7 @@ module API
|
|||
use :optional_params
|
||||
end
|
||||
post ":id/merge_requests" do
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42316')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20770')
|
||||
|
||||
authorize! :create_merge_request_from, user_project
|
||||
|
||||
|
@ -416,7 +416,7 @@ module API
|
|||
at_least_one_of(*::API::MergeRequests.update_params_at_least_one_of)
|
||||
end
|
||||
put ':id/merge_requests/:merge_request_iid' do
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42318')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20772')
|
||||
|
||||
merge_request = find_merge_request_with_access(params.delete(:merge_request_iid), :update_merge_request)
|
||||
|
||||
|
@ -445,7 +445,7 @@ module API
|
|||
optional :squash, type: Grape::API::Boolean, desc: 'When true, the commits will be squashed into a single commit on merge'
|
||||
end
|
||||
put ':id/merge_requests/:merge_request_iid/merge' do
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42317')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/4796')
|
||||
|
||||
merge_request = find_project_merge_request(params[:merge_request_iid])
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ module API
|
|||
|
||||
check_rate_limit! :project_import, [current_user, :project_import]
|
||||
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42437')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20823')
|
||||
|
||||
validate_file!
|
||||
|
||||
|
|
|
@ -310,7 +310,7 @@ module API
|
|||
optional :visibility, type: String, values: Gitlab::VisibilityLevel.string_values, desc: 'The visibility of the fork'
|
||||
end
|
||||
post ':id/fork', feature_category: :source_code_management do
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42284')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20759')
|
||||
|
||||
not_found! unless can?(current_user, :fork_project, user_project)
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ module API
|
|||
optional :variables, type: Hash, desc: 'The list of variables to be injected into build'
|
||||
end
|
||||
post ":id/(ref/:ref/)trigger/pipeline", requirements: { ref: /.+/ } do
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab-foss/issues/42283')
|
||||
Gitlab::QueryLimiting.disable!('https://gitlab.com/gitlab-org/gitlab/-/issues/20758')
|
||||
|
||||
forbidden! if gitlab_pipeline_hook_request?
|
||||
|
||||
|
|
|
@ -249,7 +249,7 @@ module Backup
|
|||
progress.puts " * #{display_repo_path} ... "
|
||||
|
||||
if repository.empty?
|
||||
progress.puts " * #{display_repo_path} ... " + "[SKIPPED]".color(:cyan)
|
||||
progress.puts " * #{display_repo_path} ... " + "[EMPTY] [SKIPPED]".color(:cyan)
|
||||
return
|
||||
end
|
||||
|
||||
|
|
|
@ -19977,6 +19977,9 @@ msgstr ""
|
|||
msgid "More Information"
|
||||
msgstr ""
|
||||
|
||||
msgid "More Information."
|
||||
msgstr ""
|
||||
|
||||
msgid "More Slack commands"
|
||||
msgstr ""
|
||||
|
||||
|
@ -34256,9 +34259,6 @@ msgstr ""
|
|||
msgid "WikiClone|Start Gollum and edit locally"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiEdit|There is already a page with the same title in that path."
|
||||
msgstr ""
|
||||
|
||||
|
@ -34331,18 +34331,6 @@ msgstr ""
|
|||
msgid "WikiHistoricalPage|most recent version"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiMarkdownDocs|More examples are in the %{docs_link}"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiMarkdownDocs|documentation"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiMarkdownTip|To link to a (new) page, simply type %{link_example}"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiNewPageTip|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPageConfirmDelete|Are you sure you want to delete this page?"
|
||||
msgstr ""
|
||||
|
||||
|
@ -34355,10 +34343,37 @@ msgstr ""
|
|||
msgid "WikiPageConflictMessage|Someone edited the page the same time you did. Please check out %{wikiLinkStart}the page%{wikiLinkEnd} and make sure your changes will not unintentionally remove theirs."
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPageCreate|Create %{pageTitle}"
|
||||
msgid "WikiPage|Commit message"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPageEdit|Update %{pageTitle}"
|
||||
msgid "WikiPage|Content"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPage|Create %{pageTitle}"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPage|Create page"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPage|Format"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPage|Page title"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPage|Tip: You can move this page by adding the path to the beginning of the title."
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPage|Tip: You can specify the full path for the new file. We will automatically create any missing directories."
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPage|Title"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPage|To link to a (new) page, simply type %{linkExample}. More examples are in the %{linkStart}documentation%{linkEnd}."
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPage|Update %{pageTitle}"
|
||||
msgstr ""
|
||||
|
||||
msgid "WikiPage|Write your content or drag files here…"
|
||||
|
@ -34370,9 +34385,6 @@ msgstr ""
|
|||
msgid "Wiki|Create New Page"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|Create page"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|Created date"
|
||||
msgstr ""
|
||||
|
||||
|
@ -34385,9 +34397,6 @@ msgstr ""
|
|||
msgid "Wiki|Page history"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|Page title"
|
||||
msgstr ""
|
||||
|
||||
msgid "Wiki|Page version"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -9,12 +9,11 @@ module QA
|
|||
def self.included(base)
|
||||
super
|
||||
|
||||
base.view 'app/views/shared/wikis/_form.html.haml' do
|
||||
base.view 'app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue' do
|
||||
element :wiki_title_textbox
|
||||
element :wiki_content_textarea
|
||||
element :wiki_message_textbox
|
||||
element :save_changes_button
|
||||
element :create_page_button
|
||||
element :wiki_submit_button
|
||||
end
|
||||
|
||||
base.view 'app/assets/javascripts/pages/shared/wikis/components/delete_wiki_modal.vue' do
|
||||
|
@ -34,12 +33,8 @@ module QA
|
|||
fill_element(:wiki_message_textbox, message)
|
||||
end
|
||||
|
||||
def click_save_changes
|
||||
click_element(:save_changes_button)
|
||||
end
|
||||
|
||||
def click_create_page
|
||||
click_element(:create_page_button)
|
||||
def click_submit
|
||||
click_element(:wiki_submit_button)
|
||||
end
|
||||
|
||||
def delete_page
|
||||
|
|
|
@ -27,7 +27,7 @@ module QA
|
|||
edit.set_message commit_message
|
||||
end
|
||||
|
||||
Page::Project::Wiki::Edit.perform(&:click_create_page)
|
||||
Page::Project::Wiki::Edit.perform(&:click_submit)
|
||||
|
||||
Page::Project::Wiki::Show.perform do |wiki|
|
||||
expect(wiki).to have_title new_wiki_title
|
||||
|
@ -46,7 +46,7 @@ module QA
|
|||
edit.set_message commit_message
|
||||
end
|
||||
|
||||
Page::Project::Wiki::Edit.perform(&:click_create_page)
|
||||
Page::Project::Wiki::Edit.perform(&:click_submit)
|
||||
|
||||
Page::Project::Wiki::Show.perform do |wiki|
|
||||
expect(wiki).to have_title new_wiki_title
|
||||
|
|
|
@ -25,7 +25,7 @@ module QA
|
|||
edit.set_message commit_message
|
||||
end
|
||||
|
||||
Page::Project::Wiki::Edit.perform(&:click_save_changes)
|
||||
Page::Project::Wiki::Edit.perform(&:click_submit)
|
||||
|
||||
Page::Project::Wiki::Show.perform do |wiki|
|
||||
expect(wiki).to have_title new_wiki_title
|
||||
|
|
|
@ -20,7 +20,7 @@ module QA
|
|||
edit.set_message('changing the path of the home page')
|
||||
end
|
||||
|
||||
Page::Project::Wiki::Edit.perform(&:click_save_changes)
|
||||
Page::Project::Wiki::Edit.perform(&:click_submit)
|
||||
|
||||
Page::Project::Wiki::Show.perform do |wiki|
|
||||
expect(wiki).to have_directory('a')
|
||||
|
|
167
scripts/perf/query_limiting_report.rb
Executable file
167
scripts/perf/query_limiting_report.rb
Executable file
|
@ -0,0 +1,167 @@
|
|||
#!/usr/bin/env ruby
|
||||
# frozen_string_literal: true
|
||||
|
||||
####
|
||||
# Prints a report which helps reconcile occurrences of the `QueryLimiting.disable(ISSUE_LINK)`
|
||||
# allowlist block against the corresponding open issues.
|
||||
#
|
||||
# If everything is consistent, the script should ideally not report any issues or code lines,
|
||||
# other than possibly remaining "calls with no issue iid" which use variables/etc.
|
||||
#
|
||||
# - See https://gitlab.com/gitlab-org/gitlab/-/issues/325640
|
||||
# - See https://gitlab.com/groups/gitlab-org/-/epics/5670
|
||||
|
||||
require 'rubygems'
|
||||
require 'gitlab'
|
||||
require 'optparse'
|
||||
|
||||
class QueryLimitingReport
|
||||
GITLAB_PROJECT_ID = 278964 # gitlab-org/gitlab project
|
||||
ISSUES_SEARCH_LABEL = 'querylimiting-disable'
|
||||
CODE_LINES_SEARCH_STRING = 'QueryLimiting.disable'
|
||||
PAGINATION_LIMIT = 500
|
||||
|
||||
DEFAULT_OPTIONS = {
|
||||
api_token: ENV['API_TOKEN']
|
||||
}.freeze
|
||||
|
||||
def initialize(options)
|
||||
@options = options
|
||||
|
||||
Gitlab.configure do |config|
|
||||
config.endpoint = 'https://gitlab.com/api/v4'
|
||||
config.private_token = options.fetch(:api_token)
|
||||
end
|
||||
end
|
||||
|
||||
def execute
|
||||
# PLAN:
|
||||
# Read all issues matching criteria and extract array of issue iids
|
||||
# Find all code references and extract issue iids
|
||||
# Print list of all issues without code references
|
||||
# Print list of all code references issue iids that don't have search label
|
||||
# Print list of all code references with no issue iids (i.e. dynamic or variable argument)
|
||||
|
||||
total_issues = find_issues_by_label(ISSUES_SEARCH_LABEL)
|
||||
issues = total_issues.select { |issue| issue[:state] == 'opened' }
|
||||
code_lines = find_code_lines
|
||||
|
||||
code_lines_grouped = code_lines.group_by { |code_line| code_line[:has_issue_iid] }
|
||||
code_lines_without_issue_iid = code_lines_grouped[false]
|
||||
code_lines_with_issue_iid = code_lines_grouped[true]
|
||||
|
||||
all_issue_iids_in_code_lines = code_lines_with_issue_iid.map { |line| line[:issue_iid] }
|
||||
|
||||
issues_without_code_references = issues.reject do |issue|
|
||||
all_issue_iids_in_code_lines.include?(issue[:iid])
|
||||
end
|
||||
|
||||
all_issue_iids = issues.map { |issue| issue[:iid] }
|
||||
code_lines_with_missing_issues = code_lines_with_issue_iid.reject do |code_line|
|
||||
all_issue_iids.include?(code_line[:issue_iid])
|
||||
end
|
||||
|
||||
puts "\n\n\nREPORT:"
|
||||
|
||||
puts "\n\nFound #{total_issues.length} total issues with '#{ISSUES_SEARCH_LABEL}' search label, #{issues.length} are still opened..."
|
||||
puts "\n\nFound #{code_lines.length} total occurrences of '#{CODE_LINES_SEARCH_STRING}' in code..."
|
||||
|
||||
puts "\n" + '-' * 80
|
||||
|
||||
puts "\n\nIssues without any '#{CODE_LINES_SEARCH_STRING}' code references (#{issues_without_code_references.length} total):"
|
||||
pp issues_without_code_references
|
||||
|
||||
puts "\n" + '-' * 80
|
||||
|
||||
puts "\n\n'#{CODE_LINES_SEARCH_STRING}' calls with references to an issue which doesn't have '#{ISSUES_SEARCH_LABEL}' search label (#{code_lines_with_missing_issues.length} total):"
|
||||
pp code_lines_with_missing_issues
|
||||
|
||||
puts "\n" + '-' * 80
|
||||
|
||||
puts "\n\n'#{CODE_LINES_SEARCH_STRING}' calls with no issue iid (#{code_lines_without_issue_iid&.length || 0} total):"
|
||||
pp code_lines_without_issue_iid
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :options
|
||||
|
||||
def find_issues_by_label(label)
|
||||
issues = []
|
||||
|
||||
puts("Finding issues by label #{label}...")
|
||||
paginated_issues = Gitlab.issues(GITLAB_PROJECT_ID, 'labels' => label)
|
||||
paginated_issues.paginate_with_limit(PAGINATION_LIMIT) do |item|
|
||||
item_hash = item.to_hash
|
||||
|
||||
issue_iid = item_hash.fetch('iid')
|
||||
issue = {
|
||||
iid: issue_iid,
|
||||
state: item_hash.fetch('state'),
|
||||
title: item_hash.fetch('title'),
|
||||
issue_url: "https://gitlab.com/gitlab-org/gitlab/issues/#{issue_iid}"
|
||||
}
|
||||
|
||||
issues << issue
|
||||
end
|
||||
|
||||
issues
|
||||
end
|
||||
|
||||
def find_code_lines
|
||||
code_lines = []
|
||||
|
||||
puts("Finding code lines...")
|
||||
paginated_blobs = Gitlab.search_in_project(GITLAB_PROJECT_ID, 'blobs', CODE_LINES_SEARCH_STRING)
|
||||
paginated_blobs.paginate_with_limit(PAGINATION_LIMIT) do |item|
|
||||
item_hash = item.to_hash
|
||||
|
||||
filename = item_hash.fetch('filename')
|
||||
next if filename !~ /\.rb\Z/
|
||||
|
||||
file_contents = Gitlab.file_contents(GITLAB_PROJECT_ID, filename)
|
||||
file_lines = file_contents.split("\n")
|
||||
|
||||
file_lines.each_index do |index|
|
||||
line = file_lines[index]
|
||||
if line =~ /#{CODE_LINES_SEARCH_STRING}/
|
||||
issue_iid = line.slice(%r{issues/(\d+)\D}, 1)
|
||||
line_number = index + 1
|
||||
code_line = {
|
||||
file_location: "#{filename}:#{line_number}",
|
||||
filename: filename,
|
||||
line_number: line_number,
|
||||
line: line,
|
||||
issue_iid: issue_iid.to_i,
|
||||
has_issue_iid: !issue_iid.nil?
|
||||
}
|
||||
code_lines << code_line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
code_lines.sort_by! { |line| "#{line[:filename]}-#{line[:line_number].to_s.rjust(4, '0')}" }
|
||||
code_lines.map do |line|
|
||||
line.delete(:filename)
|
||||
line.delete(:line_number)
|
||||
line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if $0 == __FILE__
|
||||
options = QueryLimitingReport::DEFAULT_OPTIONS.dup
|
||||
|
||||
OptionParser.new do |opts|
|
||||
opts.on("-t", "--api-token API_TOKEN", String, "A value API token with the `read_api` scope. Can be set as an env variable 'API_TOKEN'.") do |value|
|
||||
options[:api_token] = value
|
||||
end
|
||||
|
||||
opts.on("-h", "--help", "Prints this help") do
|
||||
puts opts
|
||||
exit
|
||||
end
|
||||
end.parse!
|
||||
|
||||
QueryLimitingReport.new(options).execute
|
||||
end
|
|
@ -42,7 +42,7 @@ RSpec.describe 'Contributions Calendar', :js do
|
|||
"#{contributions} #{'contribution'.pluralize(contributions)}"
|
||||
end
|
||||
|
||||
"#{get_cell_color_selector(contributions)}[title='#{contribution_text}<br />#{date}']"
|
||||
"#{get_cell_color_selector(contributions)}[title='#{contribution_text}<br /><span class=\"gl-text-gray-300\">#{date}</span>']"
|
||||
end
|
||||
|
||||
def push_code_contribution
|
||||
|
|
222
spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
Normal file
222
spec/frontend/pages/shared/wikis/components/wiki_form_spec.js
Normal file
|
@ -0,0 +1,222 @@
|
|||
import { mount } from '@vue/test-utils';
|
||||
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
|
||||
import WikiForm from '~/pages/shared/wikis/components/wiki_form.vue';
|
||||
|
||||
describe('WikiForm', () => {
|
||||
let wrapper;
|
||||
|
||||
const findForm = () => wrapper.find('form');
|
||||
const findTitle = () => wrapper.find('#wiki_title');
|
||||
const findFormat = () => wrapper.find('#wiki_format');
|
||||
const findContent = () => wrapper.find('#wiki_content');
|
||||
const findMessage = () => wrapper.find('#wiki_message');
|
||||
const findSubmitButton = () => wrapper.findByTestId('wiki-submit-button');
|
||||
const findCancelButton = () => wrapper.findByTestId('wiki-cancel-button');
|
||||
const findTitleHelpLink = () => wrapper.findByTestId('wiki-title-help-link');
|
||||
const findMarkdownHelpLink = () => wrapper.findByTestId('wiki-markdown-help-link');
|
||||
|
||||
const pageInfoNew = {
|
||||
persisted: false,
|
||||
uploadsPath: '/project/path/-/wikis/attachments',
|
||||
wikiPath: '/project/path/-/wikis',
|
||||
helpPath: '/help/user/project/wiki/index',
|
||||
markdownHelpPath: '/help/user/markdown',
|
||||
markdownPreviewPath: '/project/path/-/wikis/.md/preview-markdown',
|
||||
createPath: '/project/path/-/wikis/new',
|
||||
};
|
||||
|
||||
const pageInfoPersisted = {
|
||||
...pageInfoNew,
|
||||
persisted: true,
|
||||
|
||||
title: 'My page',
|
||||
content: 'My page content',
|
||||
format: 'markdown',
|
||||
path: '/project/path/-/wikis/home',
|
||||
};
|
||||
|
||||
function createWrapper(persisted = false, pageInfo = {}) {
|
||||
wrapper = extendedWrapper(
|
||||
mount(
|
||||
WikiForm,
|
||||
{
|
||||
provide: {
|
||||
formatOptions: {
|
||||
Markdown: 'markdown',
|
||||
RDoc: 'rdoc',
|
||||
AsciiDoc: 'asciidoc',
|
||||
Org: 'org',
|
||||
},
|
||||
pageInfo: {
|
||||
...(persisted ? pageInfoPersisted : pageInfoNew),
|
||||
...pageInfo,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ attachToDocument: true },
|
||||
),
|
||||
);
|
||||
|
||||
jest.spyOn(wrapper.vm, 'onBeforeUnload');
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
});
|
||||
|
||||
it.each`
|
||||
title | persisted | message
|
||||
${'my page'} | ${false} | ${'Create my page'}
|
||||
${'my-page'} | ${false} | ${'Create my page'}
|
||||
${'somedir/my-page'} | ${false} | ${'Create somedir/my page'}
|
||||
${'my-page'} | ${true} | ${'Update my page'}
|
||||
`(
|
||||
'updates the commit message to $message when title is $title and persisted=$persisted',
|
||||
async ({ title, message, persisted }) => {
|
||||
createWrapper(persisted);
|
||||
|
||||
findTitle().setValue(title);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(findMessage().element.value).toBe(message);
|
||||
},
|
||||
);
|
||||
|
||||
it('sets the commit message to "Update My page" when the page first loads when persisted', async () => {
|
||||
createWrapper(true);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(findMessage().element.value).toBe('Update My page');
|
||||
});
|
||||
|
||||
it.each`
|
||||
value | text
|
||||
${'markdown'} | ${'[Link Title](page-slug)'}
|
||||
${'rdoc'} | ${'{Link title}[link:page-slug]'}
|
||||
${'asciidoc'} | ${'link:page-slug[Link title]'}
|
||||
${'org'} | ${'[[page-slug]]'}
|
||||
`('updates the link help message when format=$value is selected', async ({ value, text }) => {
|
||||
createWrapper();
|
||||
|
||||
findFormat().find(`option[value=${value}]`).setSelected();
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(wrapper.text()).toContain(text);
|
||||
});
|
||||
|
||||
it('starts with no unload warning', async () => {
|
||||
createWrapper();
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
window.dispatchEvent(new Event('beforeunload'));
|
||||
|
||||
expect(wrapper.vm.onBeforeUnload).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each`
|
||||
persisted | titleHelpText | titleHelpLink
|
||||
${true} | ${'You can move this page by adding the path to the beginning of the title.'} | ${'/help/user/project/wiki/index#moving-a-wiki-page'}
|
||||
${false} | ${'You can specify the full path for the new file. We will automatically create any missing directories.'} | ${'/help/user/project/wiki/index#creating-a-new-wiki-page'}
|
||||
`(
|
||||
'shows appropriate title help text and help link for when persisted=$persisted',
|
||||
async ({ persisted, titleHelpLink, titleHelpText }) => {
|
||||
createWrapper(persisted);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(wrapper.text()).toContain(titleHelpText);
|
||||
expect(findTitleHelpLink().attributes().href).toEqual(titleHelpLink);
|
||||
},
|
||||
);
|
||||
|
||||
it('shows correct link for wiki specific markdown docs', async () => {
|
||||
createWrapper();
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(findMarkdownHelpLink().attributes().href).toEqual(
|
||||
'/help/user/markdown#wiki-specific-markdown',
|
||||
);
|
||||
});
|
||||
|
||||
describe('when wiki content is updated', () => {
|
||||
beforeEach(() => {
|
||||
createWrapper();
|
||||
|
||||
const input = findContent();
|
||||
input.setValue('Lorem ipsum dolar sit!');
|
||||
input.element.dispatchEvent(new Event('input'));
|
||||
|
||||
return wrapper.vm.$nextTick();
|
||||
});
|
||||
|
||||
it('sets before unload warning', () => {
|
||||
window.dispatchEvent(new Event('beforeunload'));
|
||||
|
||||
expect(wrapper.vm.onBeforeUnload).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('when form submitted, unsets before unload warning', async () => {
|
||||
findForm().element.dispatchEvent(new Event('submit'));
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
window.dispatchEvent(new Event('beforeunload'));
|
||||
|
||||
expect(wrapper.vm.onBeforeUnload).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('submit button state', () => {
|
||||
it.each`
|
||||
title | content | buttonState | disabledAttr
|
||||
${'something'} | ${'something'} | ${'enabled'} | ${undefined}
|
||||
${''} | ${'something'} | ${'disabled'} | ${'disabled'}
|
||||
${'something'} | ${''} | ${'disabled'} | ${'disabled'}
|
||||
${''} | ${''} | ${'disabled'} | ${'disabled'}
|
||||
${' '} | ${' '} | ${'disabled'} | ${'disabled'}
|
||||
`(
|
||||
"when title='$title', content='$content', then the button is $buttonState'",
|
||||
async ({ title, content, disabledAttr }) => {
|
||||
createWrapper();
|
||||
|
||||
findTitle().setValue(title);
|
||||
findContent().setValue(content);
|
||||
|
||||
await wrapper.vm.$nextTick();
|
||||
|
||||
expect(findSubmitButton().attributes().disabled).toBe(disabledAttr);
|
||||
},
|
||||
);
|
||||
|
||||
it.each`
|
||||
persisted | buttonLabel
|
||||
${true} | ${'Save changes'}
|
||||
${false} | ${'Create page'}
|
||||
`('when persisted=$persisted, label is set to $buttonLabel', ({ persisted, buttonLabel }) => {
|
||||
createWrapper(persisted);
|
||||
|
||||
expect(findSubmitButton().text()).toBe(buttonLabel);
|
||||
});
|
||||
});
|
||||
|
||||
describe('cancel button state', () => {
|
||||
it.each`
|
||||
persisted | redirectLink
|
||||
${false} | ${'/project/path/-/wikis'}
|
||||
${true} | ${'/project/path/-/wikis/home'}
|
||||
`(
|
||||
'when persisted=$persisted, redirects the user to appropriate path',
|
||||
({ persisted, redirectLink }) => {
|
||||
createWrapper(persisted);
|
||||
|
||||
expect(findCancelButton().attributes().href).toEqual(redirectLink);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
|
@ -25,6 +25,7 @@ exports[`Snippet Description Edit component rendering matches the snapshot 1`] =
|
|||
|
||||
<div
|
||||
class="js-vue-markdown-field md-area position-relative gfm-form js-expanded"
|
||||
data-uploads-path=""
|
||||
>
|
||||
<markdown-header-stub
|
||||
linecontent=""
|
||||
|
|
|
@ -4,159 +4,6 @@ import Wikis from '~/pages/shared/wikis/wikis';
|
|||
import Tracking from '~/tracking';
|
||||
|
||||
describe('Wikis', () => {
|
||||
const editFormHtmlFixture = (args) => `<form class="wiki-form ${
|
||||
args.newPage ? 'js-new-wiki-page' : ''
|
||||
}">
|
||||
<input type="text" id="wiki_title" value="My title" />
|
||||
<input type="text" id="wiki_message" />
|
||||
<select class="form-control select-control" name="wiki[format]" id="wiki_format">
|
||||
<option value="markdown">Markdown</option>
|
||||
<option selected="selected" value="rdoc">RDoc</option>
|
||||
<option value="asciidoc">AsciiDoc</option>
|
||||
<option value="org">Org</option>
|
||||
</select>
|
||||
<textarea id="wiki_content"></textarea>
|
||||
<code class="js-markup-link-example">{Link title}[link:page-slug]</code>
|
||||
<input type="submit" class="js-wiki-btn-submit">
|
||||
</input>
|
||||
</form>
|
||||
`;
|
||||
|
||||
let wikis;
|
||||
let titleInput;
|
||||
let contentInput;
|
||||
let messageInput;
|
||||
let changeFormatSelect;
|
||||
let linkExample;
|
||||
|
||||
const findBeforeUnloadWarning = () => window.onbeforeunload?.();
|
||||
const findForm = () => document.querySelector('.wiki-form');
|
||||
const findSubmitButton = () => document.querySelector('.js-wiki-btn-submit');
|
||||
|
||||
describe('when the wiki page is being created', () => {
|
||||
const formHtmlFixture = editFormHtmlFixture({ newPage: true });
|
||||
|
||||
beforeEach(() => {
|
||||
setHTMLFixture(formHtmlFixture);
|
||||
|
||||
titleInput = document.getElementById('wiki_title');
|
||||
messageInput = document.getElementById('wiki_message');
|
||||
changeFormatSelect = document.querySelector('#wiki_format');
|
||||
linkExample = document.querySelector('.js-markup-link-example');
|
||||
wikis = new Wikis();
|
||||
});
|
||||
|
||||
it('binds an event listener to the title input', () => {
|
||||
wikis.handleWikiTitleChange = jest.fn();
|
||||
|
||||
titleInput.dispatchEvent(new Event('keyup'));
|
||||
|
||||
expect(wikis.handleWikiTitleChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('sets the commit message when title changes', () => {
|
||||
titleInput.value = 'My title';
|
||||
messageInput.value = '';
|
||||
|
||||
titleInput.dispatchEvent(new Event('keyup'));
|
||||
|
||||
expect(messageInput.value).toEqual('Create My title');
|
||||
});
|
||||
|
||||
it('replaces hyphens with spaces', () => {
|
||||
titleInput.value = 'my-hyphenated-title';
|
||||
titleInput.dispatchEvent(new Event('keyup'));
|
||||
|
||||
expect(messageInput.value).toEqual('Create my hyphenated title');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when the wiki page is being updated', () => {
|
||||
const formHtmlFixture = editFormHtmlFixture({ newPage: false });
|
||||
|
||||
beforeEach(() => {
|
||||
setHTMLFixture(formHtmlFixture);
|
||||
|
||||
titleInput = document.getElementById('wiki_title');
|
||||
messageInput = document.getElementById('wiki_message');
|
||||
wikis = new Wikis();
|
||||
});
|
||||
|
||||
it('sets the commit message when title changes, prefixing with "Update"', () => {
|
||||
titleInput.value = 'My title';
|
||||
messageInput.value = '';
|
||||
|
||||
titleInput.dispatchEvent(new Event('keyup'));
|
||||
|
||||
expect(messageInput.value).toEqual('Update My title');
|
||||
});
|
||||
|
||||
it.each`
|
||||
value | text
|
||||
${'markdown'} | ${'[Link Title](page-slug)'}
|
||||
${'rdoc'} | ${'{Link title}[link:page-slug]'}
|
||||
${'asciidoc'} | ${'link:page-slug[Link title]'}
|
||||
${'org'} | ${'[[page-slug]]'}
|
||||
`('updates a message when value=$value is selected', ({ value, text }) => {
|
||||
changeFormatSelect.value = value;
|
||||
changeFormatSelect.dispatchEvent(new Event('change'));
|
||||
|
||||
expect(linkExample.innerHTML).toBe(text);
|
||||
});
|
||||
|
||||
it('starts with no unload warning', () => {
|
||||
expect(findBeforeUnloadWarning()).toBeUndefined();
|
||||
});
|
||||
|
||||
describe('when wiki content is updated', () => {
|
||||
beforeEach(() => {
|
||||
contentInput = document.getElementById('wiki_content');
|
||||
contentInput.value = 'Lorem ipsum dolar sit!';
|
||||
contentInput.dispatchEvent(new Event('input'));
|
||||
});
|
||||
|
||||
it('sets before unload warning', () => {
|
||||
expect(findBeforeUnloadWarning()).toBe('');
|
||||
});
|
||||
|
||||
it('when form submitted, unsets before unload warning', () => {
|
||||
findForm().dispatchEvent(new Event('submit'));
|
||||
expect(findBeforeUnloadWarning()).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('submit button state', () => {
|
||||
beforeEach(() => {
|
||||
setHTMLFixture(editFormHtmlFixture({ newPage: true }));
|
||||
|
||||
titleInput = document.getElementById('wiki_title');
|
||||
contentInput = document.getElementById('wiki_content');
|
||||
|
||||
wikis = new Wikis();
|
||||
});
|
||||
|
||||
it.each`
|
||||
title | text | buttonState | disabledAttr
|
||||
${'something'} | ${'something'} | ${'enabled'} | ${null}
|
||||
${''} | ${'something'} | ${'disabled'} | ${'true'}
|
||||
${'something'} | ${''} | ${'disabled'} | ${'true'}
|
||||
${''} | ${''} | ${'disabled'} | ${'true'}
|
||||
${' '} | ${' '} | ${'disabled'} | ${'true'}
|
||||
`(
|
||||
"when title='$title', content='$content', then, buttonState='$buttonState'",
|
||||
({ title, text, disabledAttr }) => {
|
||||
titleInput.value = title;
|
||||
titleInput.dispatchEvent(new Event('keyup'));
|
||||
|
||||
contentInput.value = text;
|
||||
contentInput.dispatchEvent(new Event('input'));
|
||||
|
||||
expect(findSubmitButton().getAttribute('disabled')).toBe(disabledAttr);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('trackPageView', () => {
|
||||
const trackingPage = 'projects:wikis:show';
|
||||
const trackingContext = { foo: 'bar' };
|
||||
|
|
|
@ -49,307 +49,8 @@ RSpec.describe HipchatService do
|
|||
WebMock.stub_request(:post, api_url)
|
||||
end
|
||||
|
||||
it 'tests and return errors' do
|
||||
allow(hipchat).to receive(:execute).and_raise(StandardError, 'no such room')
|
||||
result = hipchat.test(push_sample_data)
|
||||
|
||||
expect(result[:success]).to be_falsey
|
||||
expect(result[:result].to_s).to eq('no such room')
|
||||
end
|
||||
|
||||
it 'uses v1 if version is provided' do
|
||||
allow(hipchat).to receive(:api_version).and_return('v1')
|
||||
expect(HipChat::Client).to receive(:new).with(
|
||||
token,
|
||||
api_version: 'v1',
|
||||
server_url: server_url
|
||||
).and_return(double(:hipchat_service).as_null_object)
|
||||
hipchat.execute(push_sample_data)
|
||||
end
|
||||
|
||||
it 'uses v2 as the version when nothing is provided' do
|
||||
allow(hipchat).to receive(:api_version).and_return('')
|
||||
expect(HipChat::Client).to receive(:new).with(
|
||||
token,
|
||||
api_version: 'v2',
|
||||
server_url: server_url
|
||||
).and_return(double(:hipchat_service).as_null_object)
|
||||
hipchat.execute(push_sample_data)
|
||||
end
|
||||
|
||||
context 'push events' do
|
||||
it "calls Hipchat API for push events" do
|
||||
hipchat.execute(push_sample_data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "creates a push message" do
|
||||
message = hipchat.send(:create_push_message, push_sample_data)
|
||||
|
||||
push_sample_data[:object_attributes]
|
||||
branch = push_sample_data[:ref].gsub('refs/heads/', '')
|
||||
expect(message).to include("#{user.name} pushed to branch " \
|
||||
"<a href=\"#{project.web_url}/commits/#{branch}\">#{branch}</a> of " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>")
|
||||
end
|
||||
end
|
||||
|
||||
context 'tag_push events' do
|
||||
let(:push_sample_data) do
|
||||
Gitlab::DataBuilder::Push.build(
|
||||
project: project,
|
||||
user: user,
|
||||
oldrev: Gitlab::Git::BLANK_SHA,
|
||||
newrev: '1' * 40,
|
||||
ref: 'refs/tags/test')
|
||||
end
|
||||
|
||||
it "calls Hipchat API for tag push events" do
|
||||
hipchat.execute(push_sample_data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "creates a tag push message" do
|
||||
message = hipchat.send(:create_push_message, push_sample_data)
|
||||
|
||||
push_sample_data[:object_attributes]
|
||||
expect(message).to eq("#{user.name} pushed new tag " \
|
||||
"<a href=\"#{project.web_url}/commits/test\">test</a> to " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>\n")
|
||||
end
|
||||
end
|
||||
|
||||
context 'issue events' do
|
||||
let(:issue) { create(:issue, title: 'Awesome issue', description: '**please** fix') }
|
||||
let(:issue_service) { Issues::CreateService.new(project, user) }
|
||||
let(:issues_sample_data) { issue_service.hook_data(issue, 'open') }
|
||||
|
||||
it "calls Hipchat API for issue events" do
|
||||
hipchat.execute(issues_sample_data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "creates an issue message" do
|
||||
message = hipchat.send(:create_issue_message, issues_sample_data)
|
||||
|
||||
obj_attr = issues_sample_data[:object_attributes]
|
||||
expect(message).to eq("#{user.name} opened " \
|
||||
"<a href=\"#{obj_attr[:url]}\">issue ##{obj_attr["iid"]}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"<b>Awesome issue</b>" \
|
||||
"<pre><strong>please</strong> fix</pre>")
|
||||
end
|
||||
end
|
||||
|
||||
context 'merge request events' do
|
||||
let(:merge_request) { create(:merge_request, description: '**please** fix', title: 'Awesome merge request', target_project: project, source_project: project) }
|
||||
let(:merge_service) { MergeRequests::CreateService.new(project, user) }
|
||||
let(:merge_sample_data) { merge_service.hook_data(merge_request, 'open') }
|
||||
|
||||
it "calls Hipchat API for merge requests events" do
|
||||
hipchat.execute(merge_sample_data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "creates a merge request message" do
|
||||
message = hipchat.send(:create_merge_request_message,
|
||||
merge_sample_data)
|
||||
|
||||
obj_attr = merge_sample_data[:object_attributes]
|
||||
expect(message).to eq("#{user.name} opened " \
|
||||
"<a href=\"#{obj_attr[:url]}\">merge request !#{obj_attr["iid"]}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"<b>Awesome merge request</b>" \
|
||||
"<pre><strong>please</strong> fix</pre>")
|
||||
end
|
||||
end
|
||||
|
||||
context "Note events" do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :repository, creator: user) }
|
||||
|
||||
context 'when commit comment event triggered' do
|
||||
let(:commit_note) do
|
||||
create(:note_on_commit, author: user, project: project,
|
||||
commit_id: project.repository.commit.id,
|
||||
note: 'a comment on a commit')
|
||||
end
|
||||
|
||||
it "calls Hipchat API for commit comment events" do
|
||||
data = Gitlab::DataBuilder::Note.build(commit_note, user)
|
||||
hipchat.execute(data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
|
||||
message = hipchat.send(:create_message, data)
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
commit_id = Commit.truncate_sha(data[:commit][:id])
|
||||
title = hipchat.send(:format_title, data[:commit][:message])
|
||||
|
||||
expect(message).to eq("#{user.name} commented on " \
|
||||
"<a href=\"#{obj_attr[:url]}\">commit #{commit_id}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"#{title}" \
|
||||
"<pre>a comment on a commit</pre>")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when merge request comment event triggered' do
|
||||
let(:merge_request) do
|
||||
create(:merge_request, source_project: project,
|
||||
target_project: project)
|
||||
end
|
||||
|
||||
let(:merge_request_note) do
|
||||
create(:note_on_merge_request, noteable: merge_request,
|
||||
project: project,
|
||||
note: "merge request **note**")
|
||||
end
|
||||
|
||||
it "calls Hipchat API for merge request comment events" do
|
||||
data = Gitlab::DataBuilder::Note.build(merge_request_note, user)
|
||||
hipchat.execute(data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
|
||||
message = hipchat.send(:create_message, data)
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
merge_id = data[:merge_request]['iid']
|
||||
title = data[:merge_request]['title']
|
||||
|
||||
expect(message).to eq("#{user.name} commented on " \
|
||||
"<a href=\"#{obj_attr[:url]}\">merge request !#{merge_id}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"<b>#{title}</b>" \
|
||||
"<pre>merge request <strong>note</strong></pre>")
|
||||
end
|
||||
end
|
||||
|
||||
context 'when issue comment event triggered' do
|
||||
let(:issue) { create(:issue, project: project) }
|
||||
let(:issue_note) do
|
||||
create(:note_on_issue, noteable: issue, project: project,
|
||||
note: "issue **note**")
|
||||
end
|
||||
|
||||
it "calls Hipchat API for issue comment events" do
|
||||
data = Gitlab::DataBuilder::Note.build(issue_note, user)
|
||||
hipchat.execute(data)
|
||||
|
||||
message = hipchat.send(:create_message, data)
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
issue_id = data[:issue]['iid']
|
||||
title = data[:issue]['title']
|
||||
|
||||
expect(message).to eq("#{user.name} commented on " \
|
||||
"<a href=\"#{obj_attr[:url]}\">issue ##{issue_id}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"<b>#{title}</b>" \
|
||||
"<pre>issue <strong>note</strong></pre>")
|
||||
end
|
||||
|
||||
context 'with confidential issue' do
|
||||
before do
|
||||
issue.update!(confidential: true)
|
||||
end
|
||||
|
||||
it 'calls Hipchat API with issue comment' do
|
||||
data = Gitlab::DataBuilder::Note.build(issue_note, user)
|
||||
hipchat.execute(data)
|
||||
|
||||
message = hipchat.send(:create_message, data)
|
||||
|
||||
expect(message).to include("<pre>issue <strong>note</strong></pre>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when snippet comment event triggered' do
|
||||
let(:snippet) { create(:project_snippet, project: project) }
|
||||
let(:snippet_note) do
|
||||
create(:note_on_project_snippet, noteable: snippet,
|
||||
project: project,
|
||||
note: "snippet note")
|
||||
end
|
||||
|
||||
it "calls Hipchat API for snippet comment events" do
|
||||
data = Gitlab::DataBuilder::Note.build(snippet_note, user)
|
||||
hipchat.execute(data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
|
||||
message = hipchat.send(:create_message, data)
|
||||
|
||||
obj_attr = data[:object_attributes]
|
||||
snippet_id = data[:snippet]['id']
|
||||
title = data[:snippet]['title']
|
||||
|
||||
expect(message).to eq("#{user.name} commented on " \
|
||||
"<a href=\"#{obj_attr[:url]}\">snippet ##{snippet_id}</a> in " \
|
||||
"<a href=\"#{project.web_url}\">#{project_name}</a>: " \
|
||||
"<b>#{title}</b>" \
|
||||
"<pre>snippet note</pre>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'pipeline events' do
|
||||
let(:pipeline) { create(:ci_empty_pipeline, user: project.owner) }
|
||||
let(:data) { Gitlab::DataBuilder::Pipeline.build(pipeline) }
|
||||
|
||||
context 'for failed' do
|
||||
before do
|
||||
pipeline.drop
|
||||
end
|
||||
|
||||
it "calls Hipchat API" do
|
||||
hipchat.execute(data)
|
||||
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "creates a build message" do
|
||||
message = hipchat.__send__(:create_pipeline_message, data)
|
||||
|
||||
project_url = project.web_url
|
||||
project_name = project.full_name.gsub(/\s/, '')
|
||||
pipeline_attributes = data[:object_attributes]
|
||||
ref = pipeline_attributes[:ref]
|
||||
ref_type = pipeline_attributes[:tag] ? 'tag' : 'branch'
|
||||
duration = pipeline_attributes[:duration]
|
||||
user_name = data[:user][:name]
|
||||
|
||||
expect(message).to eq("<a href=\"#{project_url}\">#{project_name}</a>: " \
|
||||
"Pipeline <a href=\"#{project_url}/-/pipelines/#{pipeline.id}\">##{pipeline.id}</a> " \
|
||||
"of <a href=\"#{project_url}/-/commits/#{ref}\">#{ref}</a> #{ref_type} " \
|
||||
"by #{user_name} failed in #{duration} second(s)")
|
||||
end
|
||||
end
|
||||
|
||||
context 'for succeeded' do
|
||||
before do
|
||||
pipeline.succeed
|
||||
end
|
||||
|
||||
it "calls Hipchat API" do
|
||||
hipchat.notify_only_broken_pipelines = false
|
||||
hipchat.execute(data)
|
||||
expect(WebMock).to have_requested(:post, api_url).once
|
||||
end
|
||||
|
||||
it "notifies only broken" do
|
||||
hipchat.notify_only_broken_pipelines = true
|
||||
hipchat.execute(data)
|
||||
expect(WebMock).not_to have_requested(:post, api_url).once
|
||||
end
|
||||
end
|
||||
it 'does nothing' do
|
||||
expect { hipchat.execute(push_sample_data) }.not_to raise_error
|
||||
end
|
||||
|
||||
describe "#message_options" do
|
||||
|
@ -388,22 +89,4 @@ RSpec.describe HipchatService do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'with UrlBlocker' do
|
||||
let(:user) { create(:user) }
|
||||
let(:project) { create(:project, :repository) }
|
||||
let(:hipchat) { create(:hipchat_service, project: project, properties: { room: 'test' }) }
|
||||
let(:push_sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
|
||||
|
||||
describe '#execute' do
|
||||
before do
|
||||
hipchat.server = 'http://localhost:9123'
|
||||
end
|
||||
|
||||
it 'raises UrlBlocker for localhost' do
|
||||
expect(Gitlab::UrlBlocker).to receive(:validate!).and_call_original
|
||||
expect { hipchat.execute(push_sample_data) }.to raise_error(Gitlab::HTTP::BlockedUrlError)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -240,7 +240,7 @@ RSpec.shared_examples 'User creates wiki page' do
|
|||
end
|
||||
end
|
||||
|
||||
it "shows the emoji autocompletion dropdown" do
|
||||
it "shows the emoji autocompletion dropdown", :js do
|
||||
click_link("New page")
|
||||
|
||||
page.within(".wiki-form") do
|
||||
|
|
|
@ -38,19 +38,19 @@ RSpec.shared_examples 'User previews wiki changes' do
|
|||
end
|
||||
end
|
||||
|
||||
context "when there are no spaces or hyphens in the page name" do
|
||||
context "when there are no spaces or hyphens in the page name", :js do
|
||||
let(:wiki_page) { build(:wiki_page, wiki: wiki, title: 'a/b/c/d', content: page_content) }
|
||||
|
||||
it_behaves_like 'rewrites relative links'
|
||||
end
|
||||
|
||||
context "when there are spaces in the page name" do
|
||||
context "when there are spaces in the page name", :js do
|
||||
let(:wiki_page) { build(:wiki_page, wiki: wiki, title: 'a page/b page/c page/d page', content: page_content) }
|
||||
|
||||
it_behaves_like 'rewrites relative links'
|
||||
end
|
||||
|
||||
context "when there are hyphens in the page name" do
|
||||
context "when there are hyphens in the page name", :js do
|
||||
let(:wiki_page) { build(:wiki_page, wiki: wiki, title: 'a-page/b-page/c-page/d-page', content: page_content) }
|
||||
|
||||
it_behaves_like 'rewrites relative links'
|
||||
|
|
|
@ -11,7 +11,7 @@ RSpec.shared_examples 'User updates wiki page' do
|
|||
sign_in(user)
|
||||
end
|
||||
|
||||
context 'when wiki is empty' do
|
||||
context 'when wiki is empty', :js do
|
||||
before do |example|
|
||||
visit(wiki_path(wiki))
|
||||
|
||||
|
@ -57,7 +57,7 @@ RSpec.shared_examples 'User updates wiki page' do
|
|||
it_behaves_like 'wiki file attachments'
|
||||
end
|
||||
|
||||
context 'when wiki is not empty' do
|
||||
context 'when wiki is not empty', :js do
|
||||
let!(:wiki_page) { create(:wiki_page, wiki: wiki, title: 'home', content: 'Home page') }
|
||||
|
||||
before do
|
||||
|
@ -147,7 +147,7 @@ RSpec.shared_examples 'User updates wiki page' do
|
|||
it_behaves_like 'wiki file attachments'
|
||||
end
|
||||
|
||||
context 'when the page is in a subdir' do
|
||||
context 'when the page is in a subdir', :js do
|
||||
let(:page_name) { 'page_name' }
|
||||
let(:page_dir) { "foo/bar/#{page_name}" }
|
||||
let!(:wiki_page) { create(:wiki_page, wiki: wiki, title: page_dir, content: 'Home page') }
|
||||
|
|
|
@ -24,7 +24,7 @@ RSpec.shared_examples 'User views empty wiki' do
|
|||
# This mirrors the logic in:
|
||||
# - app/views/shared/empty_states/_wikis.html.haml
|
||||
# - WikiHelper#wiki_empty_state_messages
|
||||
it 'shows the empty state message with the expected elements' do
|
||||
it 'shows the empty state message with the expected elements', :js do
|
||||
visit wiki_path(wiki)
|
||||
|
||||
if writable
|
||||
|
|
Loading…
Reference in a new issue