Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-05-12 00:08:09 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-12 00:08:09 +0300
commitfdc26e021b1e3eea4161bf6891f3a151fb7414b0 (patch)
treef06ce58930f41f8d031e827df198fed5dfab09be /app
parent11df4bf91b8cf9ac7bb601241992e300eebf684c (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_kroki.js4
-rw-r--r--app/assets/javascripts/behaviors/markdown/render_math.js4
-rw-r--r--app/assets/javascripts/blob/blob_line_permalink_updater.js5
-rw-r--r--app/assets/javascripts/blob/viewer/index.js17
-rw-r--r--app/assets/javascripts/breadcrumb.js2
-rw-r--r--app/assets/javascripts/code_navigation/utils/index.js4
-rw-r--r--app/assets/javascripts/deprecated_jquery_dropdown/render.js8
-rw-r--r--app/assets/javascripts/diff.js2
-rw-r--r--app/assets/javascripts/filtered_search/available_dropdown_mappings.js4
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_hint.js4
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_operator.js2
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_user.js4
-rw-r--r--app/assets/javascripts/filtered_search/dropdown_utils.js2
-rw-r--r--app/assets/javascripts/filtered_search/droplab/drop_down.js4
-rw-r--r--app/assets/javascripts/filtered_search/filtered_search_manager.js2
-rw-r--r--app/assets/javascripts/image_diff/helpers/dom_helper.js2
-rw-r--r--app/assets/javascripts/issues/create_merge_request_dropdown.js5
-rw-r--r--app/assets/javascripts/issues/show/components/description.vue2
-rw-r--r--app/assets/javascripts/lazy_loader.js9
-rw-r--r--app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js2
-rw-r--r--app/assets/javascripts/members/components/table/role_dropdown.vue2
-rw-r--r--app/assets/javascripts/pages/registrations/new/index.js5
-rw-r--r--app/assets/javascripts/pages/shared/nav/sidebar_tracking.js10
-rw-r--r--app/assets/javascripts/pages/users/activity_calendar.js2
-rw-r--r--app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue18
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue2
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue25
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue3
-rw-r--r--app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue8
-rw-r--r--app/assets/javascripts/projects/commits/components/author_select.vue2
-rw-r--r--app/assets/javascripts/sidebar/mount_sidebar.js4
-rw-r--r--app/assets/javascripts/terraform/index.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_icon.vue33
-rw-r--r--app/assets/javascripts/whats_new/components/app.vue2
-rw-r--r--app/assets/javascripts/whats_new/utils/notification.js2
-rw-r--r--app/assets/stylesheets/bootstrap_migration_components.scss9
-rw-r--r--app/assets/stylesheets/framework/icons.scss72
-rw-r--r--app/assets/stylesheets/framework/wells.scss2
-rw-r--r--app/assets/stylesheets/page_bundles/_pipeline_mixins.scss99
-rw-r--r--app/assets/stylesheets/page_bundles/merge_requests.scss4
-rw-r--r--app/assets/stylesheets/page_bundles/pipelines.scss65
-rw-r--r--app/assets/stylesheets/pages/commits.scss5
-rw-r--r--app/finders/error_tracking/errors_finder.rb46
-rw-r--r--app/graphql/types/ci/runner_status_enum.rb7
-rw-r--r--app/graphql/types/work_item_type.rb3
-rw-r--r--app/helpers/ci/runners_helper.rb2
-rw-r--r--app/models/ci/build.rb1
-rw-r--r--app/models/ci/namespace_settings.rb19
-rw-r--r--app/models/ci/runner.rb6
-rw-r--r--app/models/issue.rb14
-rw-r--r--app/models/namespace.rb1
-rw-r--r--app/models/namespace_ci_cd_setting.rb2
-rw-r--r--app/models/project.rb1
-rw-r--r--app/services/error_tracking/base_service.rb10
-rw-r--r--app/services/error_tracking/collect_error_service.rb26
-rw-r--r--app/services/error_tracking/issue_details_service.rb11
-rw-r--r--app/services/error_tracking/issue_latest_event_service.rb13
-rw-r--r--app/services/error_tracking/issue_update_service.rb6
-rw-r--r--app/services/error_tracking/list_issues_service.rb16
-rw-r--r--app/views/projects/_merge_request_merge_method_settings.html.haml2
61 files changed, 271 insertions, 384 deletions
diff --git a/app/assets/javascripts/behaviors/markdown/render_kroki.js b/app/assets/javascripts/behaviors/markdown/render_kroki.js
index abe71694d73..241585c30f1 100644
--- a/app/assets/javascripts/behaviors/markdown/render_kroki.js
+++ b/app/assets/javascripts/behaviors/markdown/render_kroki.js
@@ -55,8 +55,8 @@ export function renderKroki(krokiImages) {
// A single Kroki image is processed multiple times for some reason,
// so this condition ensures we only create one alert per Kroki image
- if (!parent.hasAttribute('data-kroki-processed')) {
- parent.setAttribute('data-kroki-processed', 'true');
+ if (!Object.hasOwn(parent.dataset, 'krokiProcessed')) {
+ parent.dataset.krokiProcessed = 'true';
parent.after(createAlert(krokiImage));
}
});
diff --git a/app/assets/javascripts/behaviors/markdown/render_math.js b/app/assets/javascripts/behaviors/markdown/render_math.js
index 12f47255bdf..7e1bf83f8a1 100644
--- a/app/assets/javascripts/behaviors/markdown/render_math.js
+++ b/app/assets/javascripts/behaviors/markdown/render_math.js
@@ -110,7 +110,7 @@ class SafeMathRenderer {
try {
displayContainer.innerHTML = this.katex.renderToString(text, {
- displayMode: el.getAttribute('data-math-style') === 'display',
+ displayMode: el.dataset.mathStyle === 'display',
throwOnError: true,
maxSize: 20,
maxExpand: 20,
@@ -143,7 +143,7 @@ class SafeMathRenderer {
this.elements.forEach((el) => {
const placeholder = document.createElement('span');
placeholder.style.display = 'none';
- placeholder.setAttribute('data-math-style', el.getAttribute('data-math-style'));
+ placeholder.dataset.mathStyle = el.dataset.mathStyle;
placeholder.textContent = el.textContent;
el.parentNode.replaceChild(placeholder, el);
this.queue.push(placeholder);
diff --git a/app/assets/javascripts/blob/blob_line_permalink_updater.js b/app/assets/javascripts/blob/blob_line_permalink_updater.js
index a3dd241604d..0a5bcf326a1 100644
--- a/app/assets/javascripts/blob/blob_line_permalink_updater.js
+++ b/app/assets/javascripts/blob/blob_line_permalink_updater.js
@@ -9,10 +9,11 @@ const updateLineNumbersOnBlobPermalinks = (linksToUpdate) => {
[].concat(Array.prototype.slice.call(linksToUpdate)).forEach((permalinkButton) => {
const baseHref =
- permalinkButton.getAttribute('data-original-href') ||
+ permalinkButton.dataset.originalHref ||
(() => {
const href = permalinkButton.getAttribute('href');
- permalinkButton.setAttribute('data-original-href', href);
+ // eslint-disable-next-line no-param-reassign
+ permalinkButton.dataset.originalHref = href;
return href;
})();
permalinkButton.setAttribute('href', `${baseHref}${hashUrlString}`);
diff --git a/app/assets/javascripts/blob/viewer/index.js b/app/assets/javascripts/blob/viewer/index.js
index a6eed4ecae3..a0d4f7ef4f2 100644
--- a/app/assets/javascripts/blob/viewer/index.js
+++ b/app/assets/javascripts/blob/viewer/index.js
@@ -36,19 +36,19 @@ const loadRichBlobViewer = (type) => {
const loadViewer = (viewerParam) => {
const viewer = viewerParam;
- const url = viewer.getAttribute('data-url');
+ const { url } = viewer.dataset;
- if (!url || viewer.getAttribute('data-loaded') || viewer.getAttribute('data-loading')) {
+ if (!url || viewer.dataset.loaded || viewer.dataset.loading) {
return Promise.resolve(viewer);
}
- viewer.setAttribute('data-loading', 'true');
+ viewer.dataset.loading = 'true';
return axios.get(url).then(({ data }) => {
viewer.innerHTML = data.html;
window.requestIdleCallback(() => {
- viewer.removeAttribute('data-loading');
+ delete viewer.dataset.loading;
});
return viewer;
@@ -108,7 +108,7 @@ export class BlobViewer {
switchToInitialViewer() {
const initialViewer = this.$fileHolder[0].querySelector('.blob-viewer:not(.hidden)');
- let initialViewerName = initialViewer.getAttribute('data-type');
+ let initialViewerName = initialViewer.dataset.type;
if (this.switcher && window.location.hash.indexOf('#L') === 0) {
initialViewerName = 'simple';
@@ -138,12 +138,12 @@ export class BlobViewer {
e.preventDefault();
- this.switchToViewer(target.getAttribute('data-viewer'));
+ this.switchToViewer(target.dataset.viewer);
}
toggleCopyButtonState() {
if (!this.copySourceBtn) return;
- if (this.simpleViewer.getAttribute('data-loaded')) {
+ if (this.simpleViewer.dataset.loaded) {
this.copySourceBtnTooltip.setAttribute('title', __('Copy file contents'));
this.copySourceBtn.classList.remove('disabled');
} else if (this.activeViewer === this.simpleViewer) {
@@ -199,7 +199,8 @@ export class BlobViewer {
this.$fileHolder.trigger('highlight:line');
handleLocationHash();
- viewer.setAttribute('data-loaded', 'true');
+ // eslint-disable-next-line no-param-reassign
+ viewer.dataset.loaded = 'true';
this.toggleCopyButtonState();
eventHub.$emit('showBlobInteractionZones', viewer.dataset.path);
});
diff --git a/app/assets/javascripts/breadcrumb.js b/app/assets/javascripts/breadcrumb.js
index b9d3742974c..113840dbc52 100644
--- a/app/assets/javascripts/breadcrumb.js
+++ b/app/assets/javascripts/breadcrumb.js
@@ -5,7 +5,7 @@ export const addTooltipToEl = (el) => {
if (textEl && textEl.scrollWidth > textEl.offsetWidth) {
el.setAttribute('title', el.textContent);
- el.setAttribute('data-container', 'body');
+ el.dataset.container = 'body';
el.classList.add('has-tooltip');
}
};
diff --git a/app/assets/javascripts/code_navigation/utils/index.js b/app/assets/javascripts/code_navigation/utils/index.js
index 0d72153d8fe..46038df2f86 100644
--- a/app/assets/javascripts/code_navigation/utils/index.js
+++ b/app/assets/javascripts/code_navigation/utils/index.js
@@ -32,8 +32,8 @@ export const addInteractionClass = ({ path, d, wrapTextNodes }) => {
});
if (el && !isTextNode(el)) {
- el.setAttribute('data-char-index', d.start_char);
- el.setAttribute('data-line-index', d.start_line);
+ el.dataset.charIndex = d.start_char;
+ el.dataset.lineIndex = d.start_line;
el.classList.add('cursor-pointer', 'code-navigation', 'js-code-navigation');
el.closest('.line').classList.add('code-navigation-line');
}
diff --git a/app/assets/javascripts/deprecated_jquery_dropdown/render.js b/app/assets/javascripts/deprecated_jquery_dropdown/render.js
index 37287b9d981..f10c2d82b61 100644
--- a/app/assets/javascripts/deprecated_jquery_dropdown/render.js
+++ b/app/assets/javascripts/deprecated_jquery_dropdown/render.js
@@ -107,10 +107,10 @@ function createLink(data, selected, options, index) {
}
if (options.trackSuggestionClickedLabel) {
- link.setAttribute('data-track-action', 'click_text');
- link.setAttribute('data-track-label', options.trackSuggestionClickedLabel);
- link.setAttribute('data-track-value', index);
- link.setAttribute('data-track-property', slugify(data.category || 'no-category'));
+ link.dataset.trackAction = 'click_text';
+ link.dataset.trackLabel = options.trackSuggestionClickedLabel;
+ link.dataset.trackValue = index;
+ link.dataset.trackProperty = slugify(data.category || 'no-category');
}
link.classList.toggle('is-active', selected);
diff --git a/app/assets/javascripts/diff.js b/app/assets/javascripts/diff.js
index a12829f8420..47de7a76899 100644
--- a/app/assets/javascripts/diff.js
+++ b/app/assets/javascripts/diff.js
@@ -26,7 +26,7 @@ export default class Diff {
FilesCommentButton.init($diffFile);
const firstFile = $('.files').first().get(0);
- const canCreateNote = firstFile && firstFile.hasAttribute('data-can-create-note');
+ const canCreateNote = firstFile && Object.hasOwn(firstFile.dataset, 'canCreateNote');
$diffFile.each((index, file) => initImageDiffHelper.initImageDiff(file, canCreateNote));
if (!isBound) {
diff --git a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
index b57db73a86e..3913e4e8d81 100644
--- a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
+++ b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js
@@ -197,10 +197,10 @@ export default class AvailableDropdownMappings {
}
getGroupId() {
- return this.filteredSearchInput.getAttribute('data-group-id') || '';
+ return this.filteredSearchInput.dataset.groupId || '';
}
getProjectId() {
- return this.filteredSearchInput.getAttribute('data-project-id') || '';
+ return this.filteredSearchInput.dataset.projectId || '';
}
}
diff --git a/app/assets/javascripts/filtered_search/dropdown_hint.js b/app/assets/javascripts/filtered_search/dropdown_hint.js
index 9d29782c9a7..93897b4ed6c 100644
--- a/app/assets/javascripts/filtered_search/dropdown_hint.js
+++ b/app/assets/javascripts/filtered_search/dropdown_hint.js
@@ -25,9 +25,9 @@ export default class DropdownHint extends FilteredSearchDropdown {
const { selected } = e.detail;
if (selected.tagName === 'LI') {
- if (selected.hasAttribute('data-value')) {
+ if (Object.hasOwn(selected.dataset, 'value')) {
this.dismissDropdown();
- } else if (selected.getAttribute('data-action') === 'submit') {
+ } else if (selected.dataset.action === 'submit') {
this.dismissDropdown();
this.dispatchFormSubmitEvent();
} else {
diff --git a/app/assets/javascripts/filtered_search/dropdown_operator.js b/app/assets/javascripts/filtered_search/dropdown_operator.js
index fb9f25a8c45..cd0f541b4fb 100644
--- a/app/assets/javascripts/filtered_search/dropdown_operator.js
+++ b/app/assets/javascripts/filtered_search/dropdown_operator.js
@@ -23,7 +23,7 @@ export default class DropdownOperator extends FilteredSearchDropdown {
const { selected } = e.detail;
if (selected.tagName === 'LI') {
- if (selected.hasAttribute('data-value')) {
+ if (Object.hasOwn(selected.dataset, 'value')) {
const name = FilteredSearchVisualTokens.getLastTokenPartial();
const operator = selected.dataset.value;
diff --git a/app/assets/javascripts/filtered_search/dropdown_user.js b/app/assets/javascripts/filtered_search/dropdown_user.js
index 9a23ff25eac..26507a85fa8 100644
--- a/app/assets/javascripts/filtered_search/dropdown_user.js
+++ b/app/assets/javascripts/filtered_search/dropdown_user.js
@@ -31,11 +31,11 @@ export default class DropdownUser extends DropdownAjaxFilter {
}
getGroupId() {
- return this.input.getAttribute('data-group-id');
+ return this.input.dataset.groupId;
}
getProjectId() {
- return this.input.getAttribute('data-project-id');
+ return this.input.dataset.projectId;
}
projectOrGroupId() {
diff --git a/app/assets/javascripts/filtered_search/dropdown_utils.js b/app/assets/javascripts/filtered_search/dropdown_utils.js
index c98d1f8e064..22e1604871a 100644
--- a/app/assets/javascripts/filtered_search/dropdown_utils.js
+++ b/app/assets/javascripts/filtered_search/dropdown_utils.js
@@ -87,6 +87,7 @@ export default class DropdownUtils {
}
static setDataValueIfSelected(filter, operator, selected) {
+ // eslint-disable-next-line unicorn/prefer-dom-node-dataset
const dataValue = selected.getAttribute('data-value');
if (dataValue) {
@@ -96,6 +97,7 @@ export default class DropdownUtils {
tokenValue: dataValue,
clicked: true,
options: {
+ // eslint-disable-next-line unicorn/prefer-dom-node-dataset
capitalizeTokenValue: selected.hasAttribute('data-capitalize'),
},
});
diff --git a/app/assets/javascripts/filtered_search/droplab/drop_down.js b/app/assets/javascripts/filtered_search/droplab/drop_down.js
index 05b741af191..398a7b26677 100644
--- a/app/assets/javascripts/filtered_search/droplab/drop_down.js
+++ b/app/assets/javascripts/filtered_search/droplab/drop_down.js
@@ -165,8 +165,8 @@ class DropDown {
images.forEach((image) => {
const img = image;
- img.src = img.getAttribute('data-src');
- img.removeAttribute('data-src');
+ img.src = img.dataset.src;
+ delete img.dataset.src;
});
}
}
diff --git a/app/assets/javascripts/filtered_search/filtered_search_manager.js b/app/assets/javascripts/filtered_search/filtered_search_manager.js
index 07f2c75f00a..ac2cf27e873 100644
--- a/app/assets/javascripts/filtered_search/filtered_search_manager.js
+++ b/app/assets/javascripts/filtered_search/filtered_search_manager.js
@@ -814,7 +814,7 @@ export default class FilteredSearchManager {
getUsernameParams() {
const usernamesById = {};
try {
- const attribute = this.filteredSearchInput.getAttribute('data-username-params');
+ const attribute = this.filteredSearchInput.dataset.usernameParams;
JSON.parse(attribute).forEach((user) => {
usernamesById[user.id] = user.username;
});
diff --git a/app/assets/javascripts/image_diff/helpers/dom_helper.js b/app/assets/javascripts/image_diff/helpers/dom_helper.js
index 3468a629f5a..180e927a3e7 100644
--- a/app/assets/javascripts/image_diff/helpers/dom_helper.js
+++ b/app/assets/javascripts/image_diff/helpers/dom_helper.js
@@ -6,7 +6,7 @@ export function setPositionDataAttribute(el, options) {
const positionObject = { ...JSON.parse(position), x, y, width, height };
- el.setAttribute('data-position', JSON.stringify(positionObject));
+ el.dataset.position = JSON.stringify(positionObject);
}
export function updateDiscussionAvatarBadgeNumber(discussionEl, newBadgeNumber) {
diff --git a/app/assets/javascripts/issues/create_merge_request_dropdown.js b/app/assets/javascripts/issues/create_merge_request_dropdown.js
index 3ea70c07058..7c611dbf39e 100644
--- a/app/assets/javascripts/issues/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/issues/create_merge_request_dropdown.js
@@ -81,10 +81,7 @@ export default class CreateMergeRequestDropdown {
this.init();
if (isConfidentialIssue()) {
- this.createMergeRequestButton.setAttribute(
- 'data-dropdown-trigger',
- '#create-merge-request-dropdown',
- );
+ this.createMergeRequestButton.dataset.dropdownTrigger = '#create-merge-request-dropdown';
initConfidentialMergeRequest();
}
}
diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue
index 831cef66836..7594676489f 100644
--- a/app/assets/javascripts/issues/show/components/description.vue
+++ b/app/assets/javascripts/issues/show/components/description.vue
@@ -270,7 +270,7 @@ export default {
},
setActiveTask(el) {
const { parentElement } = el;
- const lineNumbers = parentElement.getAttribute('data-sourcepos').match(/\b\d+(?=:)/g);
+ const lineNumbers = parentElement.dataset.sourcepos.match(/\b\d+(?=:)/g);
this.activeTask = {
title: parentElement.innerText,
lineNumberStart: lineNumbers[0],
diff --git a/app/assets/javascripts/lazy_loader.js b/app/assets/javascripts/lazy_loader.js
index 2b4dd205cf1..ba801082377 100644
--- a/app/assets/javascripts/lazy_loader.js
+++ b/app/assets/javascripts/lazy_loader.js
@@ -127,7 +127,7 @@ export default class LazyLoader {
// Loading Images which are in the current viewport or close to them
this.lazyImages = this.lazyImages.filter((selectedImage) => {
- if (selectedImage.getAttribute('data-src')) {
+ if (selectedImage.dataset.src) {
const imgBoundRect = selectedImage.getBoundingClientRect();
const imgTop = scrollTop + imgBoundRect.top;
const imgBound = imgTop + imgBoundRect.height;
@@ -156,16 +156,17 @@ export default class LazyLoader {
}
static loadImage(img) {
- if (img.getAttribute('data-src')) {
+ if (img.dataset.src) {
img.setAttribute('loading', 'lazy');
- let imgUrl = img.getAttribute('data-src');
+ let imgUrl = img.dataset.src;
// Only adding width + height for avatars for now
if (imgUrl.indexOf('/avatar/') > -1 && imgUrl.indexOf('?') === -1) {
const targetWidth = img.getAttribute('width') || img.width;
imgUrl += `?width=${targetWidth}`;
}
img.setAttribute('src', imgUrl);
- img.removeAttribute('data-src');
+ // eslint-disable-next-line no-param-reassign
+ delete img.dataset.src;
img.classList.remove('lazy');
img.classList.add('js-lazy-loaded');
img.classList.add('qa-js-lazy-loaded');
diff --git a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js
index 1adb6f9c26f..a0e730e9722 100644
--- a/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js
+++ b/app/assets/javascripts/lib/utils/confirm_via_gl_modal/confirm_via_gl_modal.js
@@ -52,7 +52,7 @@ export function confirmAction(
export function confirmViaGlModal(message, element) {
const primaryBtnConfig = {};
- const confirmBtnVariant = element.getAttribute('data-confirm-btn-variant');
+ const { confirmBtnVariant } = element.dataset;
if (confirmBtnVariant) {
primaryBtnConfig.primaryBtnVariant = confirmBtnVariant;
diff --git a/app/assets/javascripts/members/components/table/role_dropdown.vue b/app/assets/javascripts/members/components/table/role_dropdown.vue
index fa895cf24c4..6cd8bf57313 100644
--- a/app/assets/javascripts/members/components/table/role_dropdown.vue
+++ b/app/assets/javascripts/members/components/table/role_dropdown.vue
@@ -41,7 +41,7 @@ export default {
const dropdownToggle = this.$refs.glDropdown.$el.querySelector('.dropdown-toggle');
if (dropdownToggle) {
- dropdownToggle.setAttribute('data-qa-selector', 'access_level_dropdown');
+ dropdownToggle.dataset.qaSelector = 'access_level_dropdown';
}
},
methods: {
diff --git a/app/assets/javascripts/pages/registrations/new/index.js b/app/assets/javascripts/pages/registrations/new/index.js
index 8bbe81a9ed5..94a5c1cb29b 100644
--- a/app/assets/javascripts/pages/registrations/new/index.js
+++ b/app/assets/javascripts/pages/registrations/new/index.js
@@ -3,9 +3,14 @@ import { trackNewRegistrations } from '~/google_tag_manager';
import NoEmojiValidator from '~/emoji/no_emoji_validator';
import LengthValidator from '~/pages/sessions/new/length_validator';
import UsernameValidator from '~/pages/sessions/new/username_validator';
+import Tracking from '~/tracking';
new UsernameValidator(); // eslint-disable-line no-new
new LengthValidator(); // eslint-disable-line no-new
new NoEmojiValidator(); // eslint-disable-line no-new
trackNewRegistrations();
+
+Tracking.enableFormTracking({
+ forms: { allow: ['new_user'] },
+});
diff --git a/app/assets/javascripts/pages/shared/nav/sidebar_tracking.js b/app/assets/javascripts/pages/shared/nav/sidebar_tracking.js
index 79ce1a37d21..47aae36ecbb 100644
--- a/app/assets/javascripts/pages/shared/nav/sidebar_tracking.js
+++ b/app/assets/javascripts/pages/shared/nav/sidebar_tracking.js
@@ -1,6 +1,6 @@
function onSidebarLinkClick() {
const setDataTrackAction = (element, action) => {
- element.setAttribute('data-track-action', action);
+ element.dataset.trackAction = action;
};
const setDataTrackExtra = (element, value) => {
@@ -12,10 +12,10 @@ function onSidebarLinkClick() {
? SIDEBAR_COLLAPSED
: SIDEBAR_EXPANDED;
- element.setAttribute(
- 'data-track-extra',
- JSON.stringify({ sidebar_display: sidebarCollapsed, menu_display: value }),
- );
+ element.dataset.trackExtra = JSON.stringify({
+ sidebar_display: sidebarCollapsed,
+ menu_display: value,
+ });
};
const EXPANDED = 'Expanded';
diff --git a/app/assets/javascripts/pages/users/activity_calendar.js b/app/assets/javascripts/pages/users/activity_calendar.js
index 996e12bc105..94506d33b33 100644
--- a/app/assets/javascripts/pages/users/activity_calendar.js
+++ b/app/assets/javascripts/pages/users/activity_calendar.js
@@ -298,7 +298,7 @@ export default class ActivityCalendar {
.querySelector(this.activitiesContainer)
.querySelectorAll('.js-localtime')
.forEach((el) => {
- el.setAttribute('title', formatDate(el.getAttribute('data-datetime')));
+ el.setAttribute('title', formatDate(el.dataset.datetime));
});
})
.catch(() =>
diff --git a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
index 1f2fdb7ce36..7beabcfe403 100644
--- a/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
+++ b/app/assets/javascripts/pipeline_editor/components/header/pipeline_editor_mini_graph.vue
@@ -47,6 +47,12 @@ export default {
downstreamPipelines() {
return this.linkedPipelines?.downstream?.nodes || [];
},
+ hasDownstreamPipelines() {
+ return this.downstreamPipelines.length > 0;
+ },
+ hasPipelineStages() {
+ return this.pipelineStages.length > 0;
+ },
pipelinePath() {
return this.pipeline.detailedStatus?.detailsPath || '';
},
@@ -73,9 +79,6 @@ export default {
};
});
},
- showDownstreamPipelines() {
- return this.downstreamPipelines.length > 0;
- },
upstreamPipeline() {
return this.linkedPipelines?.upstream;
},
@@ -84,7 +87,10 @@ export default {
</script>
<template>
- <div v-if="pipelineStages.length > 0" class="stage-cell gl-mr-5">
+ <div
+ v-if="hasPipelineStages"
+ class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell gl-mr-5"
+ >
<linked-pipelines-mini-list
v-if="upstreamPipeline"
:triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
@@ -92,9 +98,9 @@ export default {
] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
data-testid="pipeline-editor-mini-graph-upstream"
/>
- <pipeline-mini-graph class="gl-display-inline" :stages="pipelineStages" />
+ <pipeline-mini-graph :stages="pipelineStages" />
<linked-pipelines-mini-list
- v-if="showDownstreamPipelines"
+ v-if="hasDownstreamPipelines"
:triggered="downstreamPipelines"
:pipeline-path="pipelinePath"
data-testid="pipeline-editor-mini-graph-downstream"
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue
index 2b33467e948..6ed9d8d4dc9 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_mini_graph.vue
@@ -36,7 +36,7 @@ export default {
};
</script>
<template>
- <div data-testid="pipeline-mini-graph">
+ <div data-testid="pipeline-mini-graph" class="gl-display-inline-flex gl-flex-wrap gl-my-1">
<div
v-for="stage in stages"
:key="stage.name"
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue
index 23e2a36ddeb..53e21d4ce8b 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipeline_stage.vue
@@ -12,7 +12,8 @@
* 4. Commit widget
*/
-import { GlDropdown, GlLoadingIcon, GlTooltipDirective, GlIcon } from '@gitlab/ui';
+import { GlDropdown, GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
+import CiIcon from '~/vue_shared/components/ci_icon.vue';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __, sprintf } from '~/locale';
@@ -21,7 +22,7 @@ import JobItem from './job_item.vue';
export default {
components: {
- GlIcon,
+ CiIcon,
GlLoadingIcon,
GlDropdown,
JobItem,
@@ -51,14 +52,6 @@ export default {
dropdownContent: [],
};
},
- computed: {
- triggerButtonClass() {
- return `ci-status-icon-${this.stage.status.group}`;
- },
- borderlessIcon() {
- return `${this.stage.status.icon}_borderless`;
- },
- },
watch: {
updateDropdown() {
if (this.updateDropdown && this.isDropdownOpen() && !this.isLoading) {
@@ -117,14 +110,18 @@ export default {
:popper-opts="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
placement: 'bottom',
} /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
- :toggle-class="['mini-pipeline-graph-dropdown-toggle', triggerButtonClass]"
+ :toggle-class="['gl-rounded-full!']"
menu-class="mini-pipeline-graph-dropdown-menu"
@show="onShowDropdown"
>
<template #button-content>
- <span class="gl-pointer-events-none">
- <gl-icon :name="borderlessIcon" />
- </span>
+ <ci-icon
+ is-interactive
+ css-classes="gl-rounded-full"
+ :size="24"
+ :status="stage.status"
+ class="gl-align-items-center gl-display-inline-flex"
+ />
</template>
<gl-loading-icon v-if="isLoading" size="sm" />
<ul
diff --git a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
index d77c0a39bea..5fe7c48b380 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_list/pipelines_table.vue
@@ -169,7 +169,7 @@ export default {
</template>
<template #cell(stages)="{ item }">
- <div class="stage-cell">
+ <div class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell">
<!-- This empty div should be removed, see https://gitlab.com/gitlab-org/gitlab/-/issues/323488 -->
<div></div>
<linked-pipelines-mini-list
@@ -181,7 +181,6 @@ export default {
/>
<pipeline-mini-graph
v-if="item.details && item.details.stages && item.details.stages.length > 0"
- class="gl-display-inline"
:stages="item.details.stages"
:update-dropdown="updateGraphDropdown"
@pipelineActionRequestComplete="onPipelineActionRequestComplete"
diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
index efac9f3044a..6ed943d7df5 100644
--- a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
+++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
@@ -123,7 +123,7 @@ export default {
<template>
<div>
<gl-loading-icon v-if="$apollo.queries.pipeline.loading" />
- <div v-else>
+ <div v-else class="gl-align-items-center gl-display-flex">
<linked-pipelines-mini-list
v-if="upstreamPipeline"
:triggered-by="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [
@@ -132,11 +132,7 @@ export default {
data-testid="commit-box-mini-graph-upstream"
/>
- <pipeline-mini-graph
- :stages="formattedStages"
- class="gl-display-inline"
- data-testid="commit-box-mini-graph"
- />
+ <pipeline-mini-graph :stages="formattedStages" data-testid="commit-box-mini-graph" />
<linked-pipelines-mini-list
v-if="hasDownstream"
diff --git a/app/assets/javascripts/projects/commits/components/author_select.vue b/app/assets/javascripts/projects/commits/components/author_select.vue
index c8a0a3417f3..884ef732144 100644
--- a/app/assets/javascripts/projects/commits/components/author_select.vue
+++ b/app/assets/javascripts/projects/commits/components/author_select.vue
@@ -57,7 +57,7 @@ export default {
if (authorParam) {
commitsSearchInput.setAttribute('disabled', true);
- commitsSearchInput.setAttribute('data-toggle', 'tooltip');
+ commitsSearchInput.dataset.toggle = 'tooltip';
commitsSearchInput.setAttribute('title', tooltipMessage);
this.currentAuthor = authorParam;
}
diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js
index 2a7d967cb61..5e57d4c3d6e 100644
--- a/app/assets/javascripts/sidebar/mount_sidebar.js
+++ b/app/assets/javascripts/sidebar/mount_sidebar.js
@@ -119,7 +119,7 @@ function mountAssigneesComponentDeprecated(mediator) {
issuableIid: String(iid),
projectPath: fullPath,
field: el.dataset.field,
- signedIn: el.hasAttribute('data-signed-in'),
+ signedIn: Object.hasOwn(el.dataset, 'signedIn'),
issuableType:
isInIssuePage() || isInIncidentPage() || isInDesignPage()
? IssuableType.Issue
@@ -149,7 +149,7 @@ function mountAssigneesComponent() {
},
provide: {
canUpdate: editable,
- directlyInviteMembers: el.hasAttribute('data-directly-invite-members'),
+ directlyInviteMembers: Object.hasOwn(el.dataset, 'directlyInviteMembers'),
},
render: (createElement) =>
createElement('sidebar-assignees-widget', {
diff --git a/app/assets/javascripts/terraform/index.js b/app/assets/javascripts/terraform/index.js
index 34261f3c4db..64e90f9227c 100644
--- a/app/assets/javascripts/terraform/index.js
+++ b/app/assets/javascripts/terraform/index.js
@@ -39,7 +39,7 @@ export default () => {
props: {
emptyStateImage,
projectPath,
- terraformAdmin: el.hasAttribute('data-terraform-admin'),
+ terraformAdmin: Object.hasOwn(el.dataset, 'terraformAdmin'),
},
});
},
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
index c0b80eef082..3b3b46e9772 100644
--- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
+++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_pipeline.vue
@@ -276,12 +276,11 @@ export default {
</div>
</div>
<div>
- <span class="mr-widget-pipeline-graph">
- <span class="stage-cell">
+ <span class="gl-align-items-center gl-display-inline-flex mr-widget-pipeline-graph">
+ <span class="gl-align-items-center gl-display-inline-flex gl-flex-wrap stage-cell">
<linked-pipelines-mini-list v-if="triggeredBy.length" :triggered-by="triggeredBy" />
<pipeline-mini-graph
v-if="hasStages"
- class="gl-display-inline-block"
stages-class="mr-widget-pipeline-stages"
:stages="pipeline.details.stages"
:is-merge-train="isMergeTrain"
diff --git a/app/assets/javascripts/vue_shared/components/ci_icon.vue b/app/assets/javascripts/vue_shared/components/ci_icon.vue
index 07bd6019b80..9bccc49e894 100644
--- a/app/assets/javascripts/vue_shared/components/ci_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_icon.vue
@@ -11,17 +11,22 @@ import { GlIcon } from '@gitlab/ui';
* }
*
* Used in:
- * - Pipelines table Badge
- * - Pipelines table mini graph
- * - Pipeline graph
- * - Pipeline show view badge
- * - Jobs table
+ * - Extended MR Popover
* - Jobs show view header
* - Jobs show view sidebar
+ * - Jobs table
* - Linked pipelines
- * - Extended MR Popover
+ * - Pipeline graph
+ * - Pipeline mini graph
+ * - Pipeline show view badge
+ * - Pipelines table Badge
+ */
+
+/*
+ * These sizes are defined in gitlab-ui/src/scss/variables.scss
+ * under '$gl-icon-sizes'
*/
-const validSizes = [8, 12, 16, 18, 24, 32, 48, 72];
+const validSizes = [8, 12, 14, 16, 24, 32, 48, 72];
export default {
components: {
@@ -45,6 +50,11 @@ export default {
required: false,
default: false,
},
+ isInteractive: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
cssClasses: {
type: String,
required: false,
@@ -52,9 +62,9 @@ export default {
},
},
computed: {
- cssClass() {
+ wrapperStyleClasses() {
const status = this.status.group;
- return `ci-status-icon ci-status-icon-${status} js-ci-status-icon-${status}`;
+ return `ci-status-icon ci-status-icon-${status} js-ci-status-icon-${status} gl-rounded-full gl-justify-content-center`;
},
icon() {
return this.borderless ? `${this.status.icon}_borderless` : this.status.icon;
@@ -63,7 +73,10 @@ export default {
};
</script>
<template>
- <span :class="cssClass">
+ <span
+ :class="[wrapperStyleClasses, { interactive: isInteractive }]"
+ :style="{ height: `${size}px`, width: `${size}px` }"
+ >
<gl-icon :name="icon" :size="size" :class="cssClasses" :aria-label="status.icon" />
</span>
</template>
diff --git a/app/assets/javascripts/whats_new/components/app.vue b/app/assets/javascripts/whats_new/components/app.vue
index b74dba686ad..0c55cc2f8a6 100644
--- a/app/assets/javascripts/whats_new/components/app.vue
+++ b/app/assets/javascripts/whats_new/components/app.vue
@@ -33,7 +33,7 @@ export default {
this.fetchFreshItems();
const body = document.querySelector('body');
- const namespaceId = body.getAttribute('data-namespace-id');
+ const { namespaceId } = body.dataset;
this.track('click_whats_new_drawer', { label: 'namespace_id', value: namespaceId });
},
diff --git a/app/assets/javascripts/whats_new/utils/notification.js b/app/assets/javascripts/whats_new/utils/notification.js
index 66ee3b1a971..41aff202f48 100644
--- a/app/assets/javascripts/whats_new/utils/notification.js
+++ b/app/assets/javascripts/whats_new/utils/notification.js
@@ -1,6 +1,6 @@
export const STORAGE_KEY = 'display-whats-new-notification';
-export const getVersionDigest = (appEl) => appEl.getAttribute('data-version-digest');
+export const getVersionDigest = (appEl) => appEl.dataset.versionDigest;
export const setNotification = (appEl) => {
const versionDigest = getVersionDigest(appEl);
diff --git a/app/assets/stylesheets/bootstrap_migration_components.scss b/app/assets/stylesheets/bootstrap_migration_components.scss
index d4f0c96c613..676e69707c7 100644
--- a/app/assets/stylesheets/bootstrap_migration_components.scss
+++ b/app/assets/stylesheets/bootstrap_migration_components.scss
@@ -118,7 +118,14 @@ input[type='file'] {
margin-bottom: 16px;
.well-segment {
- padding: 16px;
+ padding: 1rem;
+
+ &.pipeline-info {
+ align-items: center;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.25rem;
+ }
&:not(:last-of-type) {
border-bottom: 1px solid $well-inner-border;
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index 0aeb7208c59..ca0240b6a65 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -3,6 +3,17 @@
svg {
fill: $green-500;
}
+
+ &.interactive {
+ &:hover {
+ background: $green-500;
+
+ svg {
+ --svg-status-bg: #{$green-100};
+ box-shadow: 0 0 0 1px $green-500;
+ }
+ }
+ }
}
.ci-status-icon-error,
@@ -10,6 +21,17 @@
svg {
fill: $red-500;
}
+
+ &.interactive {
+ &:hover {
+ background: $red-500;
+
+ svg {
+ --svg-status-bg: #{$red-100};
+ box-shadow: 0 0 0 1px $red-500;
+ }
+ }
+ }
}
.ci-status-icon-pending,
@@ -19,11 +41,33 @@
svg {
fill: $orange-500;
}
+
+ &.interactive {
+ &:hover {
+ background: $orange-500;
+
+ svg {
+ --svg-status-bg: #{$orange-100};
+ box-shadow: 0 0 0 1px $orange-500;
+ }
+ }
+ }
}
.ci-status-icon-running {
svg {
- fill: $blue-400;
+ fill: $blue-500;
+ }
+
+ &.interactive {
+ &:hover {
+ background: $blue-500;
+
+ svg {
+ --svg-status-bg: #{$blue-100};
+ box-shadow: 0 0 0 1px $blue-500;
+ }
+ }
}
}
@@ -32,7 +76,18 @@
.ci-status-icon-scheduled,
.ci-status-icon-manual {
svg {
- fill: $gl-text-color;
+ fill: $gray-900;
+ }
+
+ &.interactive {
+ &:hover {
+ background: $gray-900;
+
+ svg {
+ --svg-status-bg: #{$gray-100};
+ box-shadow: 0 0 0 1px $gray-900;
+ }
+ }
}
}
@@ -42,7 +97,18 @@
.ci-status-icon-skipped,
.ci-status-icon-notfound {
svg {
- fill: var(--gray-400, $gray-400);
+ fill: $gray-500;
+ }
+
+ &.interactive {
+ &:hover {
+ background: $gray-500;
+
+ svg {
+ --svg-status-bg: #{$gray-100};
+ box-shadow: 0 0 0 1px $gray-500;
+ }
+ }
}
}
diff --git a/app/assets/stylesheets/framework/wells.scss b/app/assets/stylesheets/framework/wells.scss
index b796f04750b..cfd215b81b8 100644
--- a/app/assets/stylesheets/framework/wells.scss
+++ b/app/assets/stylesheets/framework/wells.scss
@@ -39,7 +39,7 @@
.icon-container {
display: inline-block;
- margin-right: 8px;
+ margin: 0 0.5rem 0 0.25rem;
svg {
position: relative;
diff --git a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
index 594800c84fd..3327f8da632 100644
--- a/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
+++ b/app/assets/stylesheets/page_bundles/_pipeline_mixins.scss
@@ -13,105 +13,6 @@
}
}
-@mixin mini-pipeline-graph-color(
- $color-background-default,
- $color-background-hover-focus,
- $color-background-active,
- $color-foreground-default,
- $color-foreground-hover-focus,
- $color-foreground-active
-) {
- background-color: $color-background-default;
- border-color: $color-foreground-default;
-
- svg {
- fill: $color-foreground-default;
- }
-
- &:hover,
- &:focus {
- background-color: $color-background-hover-focus;
- border-color: $color-foreground-hover-focus;
-
- svg {
- fill: $color-foreground-hover-focus;
- }
- }
-
- &:active {
- background-color: $color-background-active;
- border-color: $color-foreground-active;
-
- svg {
- fill: $color-foreground-active;
- }
- }
-
- &:focus {
- box-shadow: 0 0 4px 1px $blue-300;
- }
-}
-
-@mixin mini-pipeline-item() {
- border-radius: 100px;
- background-color: var(--white, $white);
- border-width: 1px;
- border-style: solid;
- width: $ci-action-icon-size;
- height: $ci-action-icon-size;
- margin: 0;
- padding: 0;
- position: relative;
- vertical-align: middle;
-
- &:hover,
- &:active,
- &:focus {
- outline: none;
- border-width: 2px;
- }
-
- // Dropdown button animation in mini pipeline graph
- &.ci-status-icon-success {
- @include mini-pipeline-graph-color(var(--white, $white), $green-100, $green-200, $green-500, $green-600, $green-700);
- }
-
- &.ci-status-icon-failed {
- @include mini-pipeline-graph-color(var(--white, $white), $red-100, $red-200, $red-500, $red-600, $red-700);
- }
-
- &.ci-status-icon-pending,
- &.ci-status-icon-waiting-for-resource,
- &.ci-status-icon-success-with-warnings {
- @include mini-pipeline-graph-color(var(--white, $white), $orange-50, $orange-100, $orange-500, $orange-600, $orange-700);
- }
-
- &.ci-status-icon-running {
- @include mini-pipeline-graph-color(var(--white, $white), $blue-100, $blue-200, $blue-500, $blue-600, $blue-700);
- }
-
- &.ci-status-icon-canceled,
- &.ci-status-icon-scheduled,
- &.ci-status-icon-disabled,
- &.ci-status-icon-manual {
- @include mini-pipeline-graph-color(
- var(--white, $white),
- var(--gray-500, $gray-500),
- var(--gray-700, $gray-700),
- var(--gray-900, $gray-900),
- var(--gray-950, $gray-950),
- var(--black, $black)
- );
- }
-
- &.ci-status-icon-preparing,
- &.ci-status-icon-created,
- &.ci-status-icon-not-found,
- &.ci-status-icon-skipped {
- @include mini-pipeline-graph-color(var(--white, $white), var(--gray-100, $gray-100), var(--gray-200, $gray-200), var(--gray-400, $gray-400), var(--gray-500, $gray-500), var(--gray-600, $gray-600));
- }
-}
-
/**
Action icons inside dropdowns:
- mini graph in pipelines table
diff --git a/app/assets/stylesheets/page_bundles/merge_requests.scss b/app/assets/stylesheets/page_bundles/merge_requests.scss
index bd5888af3ad..bef7ecc8a7e 100644
--- a/app/assets/stylesheets/page_bundles/merge_requests.scss
+++ b/app/assets/stylesheets/page_bundles/merge_requests.scss
@@ -121,10 +121,6 @@ $tabs-holder-z-index: 250;
@include media-breakpoint-down(sm) {
flex-direction: column;
- .stage-cell .stage-container {
- margin-top: 16px;
- }
-
.dropdown .mini-pipeline-graph-dropdown-menu.dropdown-menu {
transform: initial;
}
diff --git a/app/assets/stylesheets/page_bundles/pipelines.scss b/app/assets/stylesheets/page_bundles/pipelines.scss
index 7b54be5c91f..a225a0f0061 100644
--- a/app/assets/stylesheets/page_bundles/pipelines.scss
+++ b/app/assets/stylesheets/page_bundles/pipelines.scss
@@ -73,36 +73,12 @@
// Mini Pipelines
.stage-cell {
- .mini-pipeline-graph-dropdown-toggle {
- svg {
- height: $ci-action-icon-size;
- width: $ci-action-icon-size;
- position: absolute;
- top: -1px;
- left: -1px;
- z-index: 2;
- overflow: visible;
- }
-
- &:hover,
- &:active,
- &:focus {
- svg {
- top: -2px;
- left: -2px;
- }
- }
- }
-
.stage-container {
- display: inline-block;
- position: relative;
- vertical-align: middle;
- height: $ci-action-icon-size;
- margin: 3px 0;
+ align-items: center;
+ display: inline-flex;
+ .stage-container {
- margin-left: 6px;
+ margin-left: 4px;
}
// Hack to show a button tooltip inline
@@ -118,44 +94,15 @@
&:not(:last-child) {
&::after {
content: '';
- width: 7px;
+ width: 4px;
position: absolute;
- right: -7px;
- top: 11px;
- border-bottom: 2px solid var(--border-color, $border-color);
- }
- }
-
- //delete when all pipelines are updated to new size
- &.mr-widget-pipeline-stages {
- + .stage-container {
- margin-left: 4px;
- }
-
- &:not(:last-child) {
- &::after {
- width: 4px;
- right: -4px;
- top: 11px;
- }
+ right: -4px;
+ border-bottom: 2px solid $gray-200;
}
}
}
}
-// Commit mini pipeline (HAML)
-button.mini-pipeline-graph-dropdown-toggle,
-// GlDropdown mini pipeline (Vue)
-// As the `mini-pipeline-item` mixin specificity is lower
-// than the toggle of dropdown with 'variant="link"' we add
-// classes ".gl-button.btn-link" to make it more specific
-// and avoid having the size overriden
-//
-// See https://gitlab.com/gitlab-org/gitlab/-/issues/320737
-button.gl-button.btn-link.mini-pipeline-graph-dropdown-toggle {
- @include mini-pipeline-item();
-}
-
// Action icons inside dropdowns:
// mini graph in pipelines table
// mini graph in MR widget pipeline
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index cc8ea1493fc..afe57bb26e6 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -35,9 +35,6 @@
}
.mr-widget-pipeline-graph {
- display: inline-block;
- vertical-align: middle;
-
.dropdown-menu {
margin-top: 11px;
}
@@ -45,8 +42,6 @@
}
.branch-info .commit-icon {
- margin-right: 8px;
-
svg {
top: 3px;
}
diff --git a/app/finders/error_tracking/errors_finder.rb b/app/finders/error_tracking/errors_finder.rb
deleted file mode 100644
index c361d6e2fc2..00000000000
--- a/app/finders/error_tracking/errors_finder.rb
+++ /dev/null
@@ -1,46 +0,0 @@
-# frozen_string_literal: true
-
-module ErrorTracking
- class ErrorsFinder
- def initialize(current_user, project, params)
- @current_user = current_user
- @project = project
- @params = params
- end
-
- def execute
- return ErrorTracking::Error.none unless authorized?
-
- collection = project.error_tracking_errors
- collection = by_status(collection)
- collection = sort(collection)
-
- collection.keyset_paginate(cursor: params[:cursor], per_page: limit)
- end
-
- private
-
- attr_reader :current_user, :project, :params
-
- def by_status(collection)
- if params[:status].present? && ErrorTracking::Error.statuses.key?(params[:status])
- collection.for_status(params[:status])
- else
- collection
- end
- end
-
- def authorized?
- Ability.allowed?(current_user, :read_sentry_issue, project)
- end
-
- def sort(collection)
- params[:sort] ? collection.sort_by_attribute(params[:sort]) : collection.order_id_desc
- end
-
- def limit
- # Restrict the maximum limit at 100 records.
- [(params[:limit] || 20).to_i, 100].min
- end
- end
-end
diff --git a/app/graphql/types/ci/runner_status_enum.rb b/app/graphql/types/ci/runner_status_enum.rb
index 2e65e2d4e1e..9ba680975f4 100644
--- a/app/graphql/types/ci/runner_status_enum.rb
+++ b/app/graphql/types/ci/runner_status_enum.rb
@@ -36,13 +36,8 @@ module Types
description: "Runner that has not contacted this instance within the last #{::Ci::Runner::STALE_TIMEOUT.inspect}. Only available if legacyMode is null. Will be a possible return value starting in 15.0.",
value: :stale
- value 'NOT_CONNECTED',
- description: 'Runner that has never contacted this instance.',
- deprecated: { reason: "Use NEVER_CONTACTED instead. NEVER_CONTACTED will have a slightly different scope starting in 15.0, with STALE being returned instead after #{::Ci::Runner::STALE_TIMEOUT.inspect} of no contact", milestone: '14.6' },
- value: :not_connected
-
value 'NEVER_CONTACTED',
- description: 'Runner that has never contacted this instance. Set legacyMode to null to utilize this value. Will replace NOT_CONNECTED starting in 15.0.',
+ description: 'Runner that has never contacted this instance.',
value: :never_contacted
end
end
diff --git a/app/graphql/types/work_item_type.rb b/app/graphql/types/work_item_type.rb
index da76c43a377..cd784d54959 100644
--- a/app/graphql/types/work_item_type.rb
+++ b/app/graphql/types/work_item_type.rb
@@ -19,8 +19,7 @@ module Types
field :title, GraphQL::Types::String, null: false,
description: 'Title of the work item.'
field :work_item_type, Types::WorkItems::TypeType, null: false,
- description: 'Type assigned to the work item.',
- method: :work_item_type_with_fallback # necessary until we validate the not null constraint
+ description: 'Type assigned to the work item.'
markdown_field :title_html, null: true
markdown_field :description_html, null: true
diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb
index b576b8802ae..6366ca0dfb1 100644
--- a/app/helpers/ci/runners_helper.rb
+++ b/app/helpers/ci/runners_helper.rb
@@ -17,7 +17,7 @@ module Ci
title = s_("Runners|Runner is online; last contact was %{runner_contact} ago") % { runner_contact: time_ago_in_words(contacted_at) }
icon = 'status-active'
span_class = 'gl-text-green-500'
- when :not_connected, :never_contacted
+ when :never_contacted
title = s_("Runners|Runner has never contacted this instance")
icon = 'warning-solid'
when :offline
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 5bb9f8fcfe1..11e2629e8dd 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -45,6 +45,7 @@ module Ci
has_one :runtime_metadata, class_name: 'Ci::RunningBuild', foreign_key: :build_id
has_many :trace_chunks, class_name: 'Ci::BuildTraceChunk', foreign_key: :build_id, inverse_of: :build
has_many :report_results, class_name: 'Ci::BuildReportResult', inverse_of: :build
+ has_one :namespace, through: :project
# Projects::DestroyService destroys Ci::Pipelines, which use_fast_destroy on :job_artifacts
# before we delete builds. By doing this, the relation should be empty and not fire any
diff --git a/app/models/ci/namespace_settings.rb b/app/models/ci/namespace_settings.rb
new file mode 100644
index 00000000000..d519a48311f
--- /dev/null
+++ b/app/models/ci/namespace_settings.rb
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+# CI::NamespaceSettings mixin
+#
+# This module is intended to encapsulate CI/CD settings-specific logic
+# and be prepended in the `Namespace` model
+module Ci
+ module NamespaceSettings
+ # Overridden in EE::Namespace
+ def allow_stale_runner_pruning?
+ false
+ end
+
+ # Overridden in EE::Namespace
+ def allow_stale_runner_pruning=(_value)
+ raise NotImplementedError
+ end
+ end
+end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index 5f59dbba6b2..53cf7c080ad 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -12,6 +12,7 @@ module Ci
include Gitlab::Utils::StrongMemoize
include TaggableQueries
include Presentable
+ include EachBatch
add_authentication_token_field :token, encrypted: :optional, expires_at: :compute_token_expiration, expiration_enforced?: :token_expiration_enforced?
@@ -59,7 +60,7 @@ module Ci
AVAILABLE_TYPES_LEGACY = %w[specific shared].freeze
AVAILABLE_TYPES = runner_types.keys.freeze
- AVAILABLE_STATUSES = %w[active paused online offline not_connected never_contacted stale].freeze # TODO: Remove in %15.0: not_connected. In %16.0: active, paused. Relevant issues: https://gitlab.com/gitlab-org/gitlab/-/issues/347303, https://gitlab.com/gitlab-org/gitlab/-/issues/347305, https://gitlab.com/gitlab-org/gitlab/-/issues/344648
+ AVAILABLE_STATUSES = %w[active paused online offline never_contacted stale].freeze # TODO: Remove in %16.0: active, paused. Relevant issues: https://gitlab.com/gitlab-org/gitlab/-/issues/347303, https://gitlab.com/gitlab-org/gitlab/-/issues/344648
AVAILABLE_SCOPES = (AVAILABLE_TYPES_LEGACY + AVAILABLE_TYPES + AVAILABLE_STATUSES).freeze
FORM_EDITABLE = %i[description tag_list active run_untagged locked access_level maximum_timeout_human_readable].freeze
@@ -83,7 +84,6 @@ module Ci
scope :recent, -> { where('ci_runners.created_at >= :date OR ci_runners.contacted_at >= :date', date: stale_deadline) }
scope :stale, -> { where('ci_runners.created_at < :date AND (ci_runners.contacted_at IS NULL OR ci_runners.contacted_at < :date)', date: stale_deadline) }
scope :offline, -> { where(arel_table[:contacted_at].lteq(online_contact_time_deadline)) }
- scope :not_connected, -> { where(contacted_at: nil) } # TODO: Remove in 15.0
scope :never_contacted, -> { where(contacted_at: nil) }
scope :ordered, -> { order(id: :desc) }
@@ -337,7 +337,7 @@ module Ci
# TODO Remove in %16.0 in favor of `status` for REST calls, see https://gitlab.com/gitlab-org/gitlab/-/issues/344648
def deprecated_rest_status
if contacted_at.nil?
- :not_connected
+ :never_contacted
elsif active?
online? ? :online : :offline
else
diff --git a/app/models/issue.rb b/app/models/issue.rb
index 5682ec08a76..d4eb77ef6de 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -186,8 +186,6 @@ class Issue < ApplicationRecord
after_save :ensure_metrics, unless: :importing?
after_create_commit :record_create_action, unless: :importing?
- before_validation :ensure_work_item_type
-
attr_spammable :title, spam_title: true
attr_spammable :description, spam_description: true
@@ -611,20 +609,12 @@ class Issue < ApplicationRecord
end
# Necessary until all issues are backfilled and we add a NOT NULL constraint on the DB
- def work_item_type_with_fallback
- work_item_type || WorkItems::Type.default_by_type(issue_type)
+ def work_item_type
+ super || WorkItems::Type.default_by_type(issue_type)
end
private
- # Issue create/update service provide a work item type
- # adding this callback as there might be other mechanisms to create/update issues we are not handling
- def ensure_work_item_type
- return if work_item_type
-
- self.work_item_type = WorkItems::Type.default_by_type(issue_type)
- end
-
override :persist_pg_full_text_search_vector
def persist_pg_full_text_search_vector(search_vector)
Issues::SearchData.upsert({ project_id: project_id, issue_id: id, search_vector: search_vector }, unique_by: %i(project_id issue_id))
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index f41bd6f5001..c9254aa432f 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -16,6 +16,7 @@ class Namespace < ApplicationRecord
include Namespaces::Traversal::Linear
include EachBatch
include BlocksUnsafeSerialization
+ include Ci::NamespaceSettings
# Temporary column used for back-filling project namespaces.
# Remove it once the back-filling of all project namespaces is done.
diff --git a/app/models/namespace_ci_cd_setting.rb b/app/models/namespace_ci_cd_setting.rb
index 0e044f4fb10..c9c3a3206b6 100644
--- a/app/models/namespace_ci_cd_setting.rb
+++ b/app/models/namespace_ci_cd_setting.rb
@@ -5,3 +5,5 @@ class NamespaceCiCdSetting < ApplicationRecord # rubocop:disable Gitlab/Namespac
self.primary_key = :namespace_id
end
+
+NamespaceCiCdSetting.prepend_mod
diff --git a/app/models/project.rb b/app/models/project.rb
index dd5bc9ded6a..027b2257f2e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -412,7 +412,6 @@ class Project < ApplicationRecord
has_one :operations_feature_flags_client, class_name: 'Operations::FeatureFlagsClient'
has_many :operations_feature_flags_user_lists, class_name: 'Operations::FeatureFlags::UserList'
- has_many :error_tracking_errors, inverse_of: :project, class_name: 'ErrorTracking::Error'
has_many :error_tracking_client_keys, inverse_of: :project, class_name: 'ErrorTracking::ClientKey'
has_many :timelogs
diff --git a/app/services/error_tracking/base_service.rb b/app/services/error_tracking/base_service.rb
index 598621f70e1..d2ecd0a6d5a 100644
--- a/app/services/error_tracking/base_service.rb
+++ b/app/services/error_tracking/base_service.rb
@@ -71,5 +71,15 @@ module ErrorTracking
def can_update?
can?(current_user, :update_sentry_issue, project)
end
+
+ def error_repository
+ Gitlab::ErrorTracking::ErrorRepository.build(project)
+ end
+
+ def handle_error_repository_exceptions
+ yield
+ rescue Gitlab::ErrorTracking::ErrorRepository::DatabaseError => e
+ { error: e.message }
+ end
end
end
diff --git a/app/services/error_tracking/collect_error_service.rb b/app/services/error_tracking/collect_error_service.rb
index 6376b743255..8cb3793ba97 100644
--- a/app/services/error_tracking/collect_error_service.rb
+++ b/app/services/error_tracking/collect_error_service.rb
@@ -5,30 +5,24 @@ module ErrorTracking
include Gitlab::Utils::StrongMemoize
def execute
- # Error is a way to group events based on common data like name or cause
- # of exception. We need to keep a sane balance here between taking too little
- # and too much data into group logic.
- error = project.error_tracking_errors.report_error(
- name: exception['type'], # Example: ActionView::MissingTemplate
- description: exception['value'], # Example: Missing template posts/show in...
- actor: actor, # Example: PostsController#show
- platform: event['platform'], # Example: ruby
- timestamp: timestamp
- )
-
- # The payload field contains all the data on error including stacktrace in jsonb.
- # Together with occurred_at these are 2 main attributes that we need to save here.
- error.events.create!(
- environment: event['environment'],
+ error_repository.report_error(
+ name: exception['type'],
description: exception['value'],
- level: event['level'],
+ actor: actor,
+ platform: event['platform'],
occurred_at: timestamp,
+ environment: event['environment'],
+ level: event['level'],
payload: event
)
end
private
+ def error_repository
+ Gitlab::ErrorTracking::ErrorRepository.build(project)
+ end
+
def event
@event ||= format_event(params[:event])
end
diff --git a/app/services/error_tracking/issue_details_service.rb b/app/services/error_tracking/issue_details_service.rb
index 1614c597a8e..e82ad540e57 100644
--- a/app/services/error_tracking/issue_details_service.rb
+++ b/app/services/error_tracking/issue_details_service.rb
@@ -49,13 +49,10 @@ module ErrorTracking
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/329596
#
if project_error_tracking_setting.integrated_client?
- error = project.error_tracking_errors.find(issue_id)
-
- # We use the same response format as project_error_tracking_setting
- # method below for compatibility with existing code.
- {
- issue: error.to_sentry_detailed_error
- }
+ handle_error_repository_exceptions do
+ error = error_repository.find_error(issue_id)
+ { issue: error }
+ end
else
project_error_tracking_setting.issue_details(issue_id: issue_id)
end
diff --git a/app/services/error_tracking/issue_latest_event_service.rb b/app/services/error_tracking/issue_latest_event_service.rb
index 1bf86c658fc..0290c8eac86 100644
--- a/app/services/error_tracking/issue_latest_event_service.rb
+++ b/app/services/error_tracking/issue_latest_event_service.rb
@@ -26,14 +26,13 @@ module ErrorTracking
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/329596
#
if project_error_tracking_setting.integrated_client?
- error = project.error_tracking_errors.find(issue_id)
- event = error.events.last
+ handle_error_repository_exceptions do
+ event = error_repository.last_event_for(issue_id)
- # We use the same response format as project_error_tracking_setting
- # method below for compatibility with existing code.
- {
- latest_event: event.to_sentry_error_event
- }
+ # We use the same response format as project_error_tracking_setting
+ # method below for compatibility with existing code.
+ { latest_event: event }
+ end
else
project_error_tracking_setting.issue_latest_event(issue_id: issue_id)
end
diff --git a/app/services/error_tracking/issue_update_service.rb b/app/services/error_tracking/issue_update_service.rb
index 624e5f94dde..ca5e8d656a6 100644
--- a/app/services/error_tracking/issue_update_service.rb
+++ b/app/services/error_tracking/issue_update_service.rb
@@ -84,14 +84,12 @@ module ErrorTracking
# Issue https://gitlab.com/gitlab-org/gitlab/-/issues/329596
#
if project_error_tracking_setting.integrated_client?
- error = project.error_tracking_errors.find(opts[:issue_id])
- error.status = opts[:params][:status]
- error.save!
+ updated = error_repository.update_error(opts[:issue_id], status: opts[:params][:status])
# We use the same response format as project_error_tracking_setting
# method below for compatibility with existing code.
{
- updated: true
+ updated: updated
}
else
project_error_tracking_setting.update_issue(**opts)
diff --git a/app/services/error_tracking/list_issues_service.rb b/app/services/error_tracking/list_issues_service.rb
index 1979816b88d..ca7208dba96 100644
--- a/app/services/error_tracking/list_issues_service.rb
+++ b/app/services/error_tracking/list_issues_service.rb
@@ -73,24 +73,24 @@ module ErrorTracking
if project_error_tracking_setting.integrated_client?
# We are going to support more options in the future.
# For now we implement the bare minimum for rendering the list in UI.
- filter_opts = {
- status: opts[:issue_status],
+ list_opts = {
+ filters: { status: opts[:issue_status] },
sort: opts[:sort],
limit: opts[:limit],
cursor: opts[:cursor]
}
- errors = ErrorTracking::ErrorsFinder.new(current_user, project, filter_opts).execute
+ errors, pagination = error_repository.list_errors(**list_opts)
- pagination = {}
- pagination[:next] = { cursor: errors.cursor_for_next_page } if errors.has_next_page?
- pagination[:previous] = { cursor: errors.cursor_for_previous_page } if errors.has_previous_page?
+ pagination_hash = {}
+ pagination_hash[:next] = { cursor: pagination.next } if pagination.next
+ pagination_hash[:previous] = { cursor: pagination.prev } if pagination.prev
# We use the same response format as project_error_tracking_setting
# method below for compatibility with existing code.
{
- issues: errors.map(&:to_sentry_error),
- pagination: pagination
+ issues: errors,
+ pagination: pagination_hash
}
else
project_error_tracking_setting.list_sentry_issues(**opts)
diff --git a/app/views/projects/_merge_request_merge_method_settings.html.haml b/app/views/projects/_merge_request_merge_method_settings.html.haml
index 250f7e94e84..cb660750632 100644
--- a/app/views/projects/_merge_request_merge_method_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_method_settings.html.haml
@@ -4,7 +4,7 @@
%b= s_('ProjectSettings|Merge method')
%p.text-secondary
= s_('ProjectSettings|Determine what happens to the commit history when you merge a merge request.')
- = link_to s_('ProjectSettings|Learn about commit history.'), help_page_path('user/project/merge_requests/commits.md'), target: '_blank', rel: 'noopener noreferrer'
+ = link_to s_('ProjectSettings|How do they differ?'), help_page_path('user/project/merge_requests/methods/index.md'), target: '_blank', rel: 'noopener noreferrer'
.form-check.mb-2
= form.radio_button :merge_method, :merge, class: "js-merge-method-radio form-check-input"
= label_tag :project_merge_method_merge, class: 'form-check-label' do