diff --git a/app/assets/javascripts/error_tracking/components/error_details.vue b/app/assets/javascripts/error_tracking/components/error_details.vue index 3fc3dcf6d01..52444d2c493 100644 --- a/app/assets/javascripts/error_tracking/components/error_details.vue +++ b/app/assets/javascripts/error_tracking/components/error_details.vue @@ -120,10 +120,10 @@ export default { ]), ...mapGetters('details', ['stacktrace']), firstReleaseLink() { - return `${this.error.externalBaseUrl}/releases/${this.error.firstReleaseShortVersion}`; + return `${this.error.externalBaseUrl}/releases/${this.error.firstReleaseVersion}`; }, lastReleaseLink() { - return `${this.error.externalBaseUrl}/releases/${this.error.lastReleaseShortVersion}`; + return `${this.error.externalBaseUrl}/releases/${this.error.lastReleaseVersion}`; }, showStacktrace() { return Boolean(this.stacktrace?.length); @@ -400,18 +400,18 @@ export default { -
  • +
  • {{ __('First seen') }}: - {{ __('Release') }}: {{ error.firstReleaseShortVersion.substr(0, 10) }} + {{ __('Release') }}: {{ error.firstReleaseVersion }}
  • -
  • +
  • {{ __('Last seen') }}: - {{ __('Release') }}: {{ error.lastReleaseShortVersion.substr(0, 10) }} + {{ __('Release') }}: {{ error.lastReleaseVersion }}
  • diff --git a/app/assets/javascripts/error_tracking/queries/details.query.graphql b/app/assets/javascripts/error_tracking/queries/details.query.graphql index fa579c94257..b49c5b6626d 100644 --- a/app/assets/javascripts/error_tracking/queries/details.query.graphql +++ b/app/assets/javascripts/error_tracking/queries/details.query.graphql @@ -18,8 +18,8 @@ query errorDetails($fullPath: ID!, $errorId: ID!) { } externalUrl externalBaseUrl - firstReleaseShortVersion - lastReleaseShortVersion + firstReleaseVersion + lastReleaseVersion gitlabCommit gitlabCommitPath gitlabIssuePath diff --git a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue index 36413198a06..de01821a292 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/mr_widget_suggest_pipeline.vue @@ -1,22 +1,31 @@ diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue deleted file mode 100644 index 1a2d845fab9..00000000000 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/mr_widget_pipeline_tour.vue +++ /dev/null @@ -1,143 +0,0 @@ - - diff --git a/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue b/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue index c4f389a48d9..0b91588a006 100644 --- a/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue +++ b/app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue @@ -88,6 +88,7 @@ export default { type="search" class="mb-3" autofocus + data-qa-selector="project_search_field" @input="onInput" />
    @@ -107,6 +108,7 @@ export default { :project="project" :matcher="searchQuery" class="js-project-list-item" + data-qa-selector="project_list_item" @click="projectClicked(project)" />
    diff --git a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_custom_renderer.js b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_custom_renderer.js index 10b06f48b09..92bb2c2ca7c 100644 --- a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_custom_renderer.js +++ b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/build_custom_renderer.js @@ -1,9 +1,10 @@ import renderKramdownList from './renderers/render_kramdown_list'; import renderKramdownText from './renderers/render_kramdown_text'; import renderIdentifierText from './renderers/render_identifier_text'; +import renderEmbeddedRubyText from './renderers/render_embedded_ruby_text'; const listRenderers = [renderKramdownList]; -const textRenderers = [renderKramdownText, renderIdentifierText]; +const textRenderers = [renderKramdownText, renderIdentifierText, renderEmbeddedRubyText]; const executeRenderer = (renderers, node, context) => { const availableRenderer = renderers.find(renderer => renderer.canRender(node, context)); diff --git a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/renderers/render_embedded_ruby_text.js b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/renderers/render_embedded_ruby_text.js new file mode 100644 index 00000000000..494057fc75b --- /dev/null +++ b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/renderers/render_embedded_ruby_text.js @@ -0,0 +1,13 @@ +import { buildUneditableTokens } from './build_uneditable_token'; + +const embeddedRubyRegex = /(^<%.+%>$)/; + +const canRender = ({ literal }) => { + return embeddedRubyRegex.test(literal); +}; + +const render = (_, { origin }) => { + return buildUneditableTokens(origin()); +}; + +export default { canRender, render }; diff --git a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/renderers/render_identifier_text.js b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/renderers/render_identifier_text.js index 0cd7635c807..a1c1f20b923 100644 --- a/app/assets/javascripts/vue_shared/components/rich_content_editor/services/renderers/render_identifier_text.js +++ b/app/assets/javascripts/vue_shared/components/rich_content_editor/services/renderers/render_identifier_text.js @@ -33,7 +33,7 @@ const hasExitingPotential = literal => literal.includes(']: '); const hasAdjacentExit = node => { let currentNode = node; - while (currentNode.next && currentNode.literal !== null) { + while (currentNode && currentNode.literal !== null) { if (hasExitingPotential(currentNode.literal)) { return true; } diff --git a/app/assets/stylesheets/framework/images.scss b/app/assets/stylesheets/framework/images.scss index 2c9397d363c..0fae1c7d235 100644 --- a/app/assets/stylesheets/framework/images.scss +++ b/app/assets/stylesheets/framework/images.scss @@ -20,7 +20,7 @@ width: 100%; } - $image-widths: 80 130 150 250 306 394 430; + $image-widths: 80 130 150 225 250 306 394 430; @each $width in $image-widths { &.svg-#{$width} { img, diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 2e975d5d7c6..1ed7b8580bf 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -397,6 +397,16 @@ $mr-widget-min-height: 69px; } } } + + &.mr-pipeline-suggest { + border-radius: $border-radius-default; + line-height: 20px; + border: 1px solid $border-color; + + .circle-icon-container { + color: $gl-text-color-quaternary; + } + } } .mr-widget-help { @@ -596,26 +606,6 @@ $mr-widget-min-height: 69px; } } -.mr-pipeline-suggest { - flex-wrap: wrap; - border-radius: $border-radius-default; - padding: $gl-padding; - border: 1px solid $border-color; - min-height: $mr-widget-min-height; - - @include media-breakpoint-up(md) { - align-items: center; - } - - .circle-icon-container { - color: $gl-text-color-quaternary; - } - - .popover { - z-index: 240; - } -} - .card-new-merge-request { .card-header { padding: 5px 10px; diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb index 12dd022c701..e0137accd2d 100644 --- a/app/controllers/admin/services_controller.rb +++ b/app/controllers/admin/services_controller.rb @@ -6,7 +6,7 @@ class Admin::ServicesController < Admin::ApplicationController before_action :service, only: [:edit, :update] before_action :whitelist_query_limiting, only: [:index] before_action only: :edit do - push_frontend_feature_flag(:integration_form_refactor) + push_frontend_feature_flag(:integration_form_refactor, default_enabled: true) end def index diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations_actions.rb index cf3c765a0ec..46febc44807 100644 --- a/app/controllers/concerns/integrations_actions.rb +++ b/app/controllers/concerns/integrations_actions.rb @@ -9,7 +9,7 @@ module IntegrationsActions before_action :not_found, unless: :integrations_enabled? before_action :integration, only: [:edit, :update, :test] before_action only: :edit do - push_frontend_feature_flag(:integration_form_refactor) + push_frontend_feature_flag(:integration_form_refactor, default_enabled: true) end end diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb index 50c8c6b7163..f8e7c1ed11e 100644 --- a/app/controllers/concerns/wiki_actions.rb +++ b/app/controllers/concerns/wiki_actions.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true module WikiActions + include DiffHelper include SendsBlob include Gitlab::Utils::StrongMemoize extend ActiveSupport::Concern @@ -11,8 +12,9 @@ module WikiActions before_action :authorize_admin_wiki!, only: :destroy before_action :wiki - before_action :page, only: [:show, :edit, :update, :history, :destroy] + before_action :page, only: [:show, :edit, :update, :history, :destroy, :diff] before_action :load_sidebar, except: [:pages] + before_action :set_content_class before_action only: [:show, :edit, :update] do @valid_encoding = valid_encoding? @@ -25,6 +27,8 @@ module WikiActions redirect_to wiki_path(wiki) end end + + helper_method :view_file_button, :diff_file_html_data end def new @@ -136,6 +140,19 @@ module WikiActions end # rubocop:enable Gitlab/ModuleWithInstanceVariables + # rubocop:disable Gitlab/ModuleWithInstanceVariables + def diff + return render_404 unless page + + apply_diff_view_cookie! + + @diffs = page.diffs(diff_options) + @diff_notes_disabled = true + + render 'shared/wikis/diff' + end + # rubocop:disable Gitlab/ModuleWithInstanceVariables + # rubocop:disable Gitlab/ModuleWithInstanceVariables def destroy WikiPages::DestroyService.new(container: container, current_user: current_user).execute(page) @@ -207,7 +224,7 @@ module WikiActions def page_params keys = [:id] - keys << :version_id if params[:action] == 'show' + keys << :version_id if %w[show diff].include?(params[:action]) params.values_at(*keys) end @@ -233,4 +250,25 @@ module WikiActions wiki.repository.blob_at(commit.id, params[:id]) end end + + def set_content_class + @content_class = 'limit-container-width' unless fluid_layout # rubocop:disable Gitlab/ModuleWithInstanceVariables + end + + # Override CommitsHelper#view_file_button + def view_file_button(commit_sha, *args) + path = wiki_page_path(wiki, page, version_id: page.version.id) + + helpers.link_to(path, class: 'btn') do + helpers.raw(_('View page @ ')) + helpers.content_tag(:span, Commit.truncate_sha(commit_sha), class: 'commit-sha') + end + end + + # Override DiffHelper#diff_file_html_data + def diff_file_html_data(_project, _diff_file_path, diff_commit_id) + { + blob_diff_path: wiki_page_path(wiki, page, action: :diff, version_id: diff_commit_id), + view: diff_view + } + end end diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 4a0d0d666af..6ce1dc87b79 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -12,7 +12,7 @@ class Projects::ServicesController < Projects::ApplicationController before_action :set_deprecation_notice_for_prometheus_service, only: [:edit, :update] before_action :redirect_deprecated_prometheus_service, only: [:update] before_action only: :edit do - push_frontend_feature_flag(:integration_form_refactor) + push_frontend_feature_flag(:integration_form_refactor, default_enabled: true) end respond_to :html diff --git a/app/graphql/types/diff_stats_summary_type.rb b/app/graphql/types/diff_stats_summary_type.rb index 5920f3de3da..956400fd21b 100644 --- a/app/graphql/types/diff_stats_summary_type.rb +++ b/app/graphql/types/diff_stats_summary_type.rb @@ -14,6 +14,8 @@ module Types description: 'Number of lines deleted' field :changes, GraphQL::INT_TYPE, null: false, description: 'Number of lines changed' + field :file_count, GraphQL::INT_TYPE, null: false, + description: 'Number of files changed' def changes object[:additions] + object[:deletions] diff --git a/app/graphql/types/error_tracking/sentry_detailed_error_type.rb b/app/graphql/types/error_tracking/sentry_detailed_error_type.rb index 124398f28e7..8bdd8afcbff 100644 --- a/app/graphql/types/error_tracking/sentry_detailed_error_type.rb +++ b/app/graphql/types/error_tracking/sentry_detailed_error_type.rb @@ -76,8 +76,14 @@ module Types description: 'Commit the error was last seen' field :first_release_short_version, GraphQL::STRING_TYPE, null: true, - description: 'Release version the error was first seen' + description: 'Release short version the error was first seen' field :last_release_short_version, GraphQL::STRING_TYPE, + null: true, + description: 'Release short version the error was last seen' + field :first_release_version, GraphQL::STRING_TYPE, + null: true, + description: 'Release version the error was first seen' + field :last_release_version, GraphQL::STRING_TYPE, null: true, description: 'Release version the error was last seen' field :gitlab_commit, GraphQL::STRING_TYPE, diff --git a/app/graphql/types/merge_request_type.rb b/app/graphql/types/merge_request_type.rb index 5c1f82cdf1d..c194b467363 100644 --- a/app/graphql/types/merge_request_type.rb +++ b/app/graphql/types/merge_request_type.rb @@ -153,11 +153,11 @@ module Types end def diff_stats_summary - nil_stats = { additions: 0, deletions: 0 } + nil_stats = { additions: 0, deletions: 0, file_count: 0 } return nil_stats unless object.diff_stats.present? object.diff_stats.each_with_object(nil_stats) do |status, hash| - hash.merge!(additions: status.additions, deletions: status.deletions) { |_, x, y| x + y } + hash.merge!(additions: status.additions, deletions: status.deletions, file_count: 1) { |_, x, y| x + y } end end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 2a0c2e73dd6..680a1a5a919 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -181,15 +181,11 @@ module CommitsHelper end def view_file_button(commit_sha, diff_new_path, project, replaced: false) + path = project_blob_path(project, tree_join(commit_sha, diff_new_path)) title = replaced ? _('View replaced file @ ') : _('View file @ ') - link_to( - project_blob_path(project, - tree_join(commit_sha, diff_new_path)), - class: 'btn view-file js-view-file' - ) do - raw(title) + content_tag(:span, Commit.truncate_sha(commit_sha), - class: 'commit-sha') + link_to(path, class: 'btn') do + raw(title) + content_tag(:span, truncate_sha(commit_sha), class: 'commit-sha') end end diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 4c3c4931387..65f204b3afd 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -135,8 +135,7 @@ module DiffHelper def diff_file_html_data(project, diff_file_path, diff_commit_id) { - blob_diff_path: project_blob_diff_path(project, - tree_join(diff_commit_id, diff_file_path)), + blob_diff_path: project_blob_diff_path(project, tree_join(diff_commit_id, diff_file_path)), view: diff_view } end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 9ea0b9cb584..d849ed9d076 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -27,7 +27,7 @@ module NavHelper end elsif current_path?('jobs#show') %w[page-gutter build-sidebar right-sidebar-expanded] - elsif current_controller?('wikis') && current_action?('show', 'create', 'edit', 'update', 'history', 'git_access', 'destroy') + elsif current_controller?('wikis') && current_action?('show', 'create', 'edit', 'update', 'history', 'git_access', 'destroy', 'diff') %w[page-gutter wiki-sidebar right-sidebar-expanded] else [] diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb index 13d26166e6e..8fb884919d4 100644 --- a/app/helpers/services_helper.rb +++ b/app/helpers/services_helper.rb @@ -96,7 +96,7 @@ module ServicesHelper end def integration_form_refactor? - Feature.enabled?(:integration_form_refactor, @project) + Feature.enabled?(:integration_form_refactor, @project, default_enabled: true) end def integration_form_data(integration) diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index 3c983606b73..c3ef4cbf841 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -3,6 +3,30 @@ module WikiHelper include API::Helpers::RelatedResourcesHelpers + def wiki_page_title(page, action = nil) + titles = [_('Wiki')] + + if page.persisted? + titles << page.human_title + breadcrumb_title(page.human_title) + wiki_breadcrumb_dropdown_links(page.slug) + end + + titles << action if action + page_title(*titles.reverse) + add_to_breadcrumbs(_('Wiki'), wiki_path(page.wiki)) + end + + def link_to_wiki_page(page, **options) + link_to page.human_title, wiki_page_path(page.wiki, page), **options + end + + def wiki_sidebar_toggle_button + content_tag :button, class: 'btn btn-default sidebar-toggle js-sidebar-wiki-toggle', role: 'button', type: 'button' do + sprite_icon('chevron-double-lg-left') + end + end + # Produces a pure text breadcrumb for a given page. # # page_slug - The slug of a WikiPage object. diff --git a/app/models/ci/pipeline_enums.rb b/app/models/ci/pipeline_enums.rb index a82dac440da..352dc56aac7 100644 --- a/app/models/ci/pipeline_enums.rb +++ b/app/models/ci/pipeline_enums.rb @@ -31,7 +31,7 @@ module Ci merge_request_event: 10, external_pull_request_event: 11, parent_pipeline: 12, - ondemand_scan: 13 + ondemand_dast_scan: 13 } end diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 9e4e2f68d38..3dc90edb331 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -301,6 +301,10 @@ class WikiPage version&.commit&.committed_date end + def diffs(diff_options = {}) + Gitlab::Diff::FileCollection::WikiPage.new(self, diff_options: diff_options) + end + private def serialize_front_matter(hash) diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb index 48c1ef3cefe..961a7cb1ef6 100644 --- a/app/services/merge_requests/merge_service.rb +++ b/app/services/merge_requests/merge_service.rb @@ -27,6 +27,7 @@ module MergeRequests success end end + log_info("Merge process finished on JID #{merge_jid} with state #{state}") rescue MergeError => e handle_merge_error(log_message: e.message, save_message_on_model: true) diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml index 9659d416a38..4a27284cbae 100644 --- a/app/views/discussions/_discussion.html.haml +++ b/app/views/discussions/_discussion.html.haml @@ -37,7 +37,7 @@ an outdated change in commit - %span.commit-sha= Commit.truncate_sha(discussion.commit_id) + %span.commit-sha= truncate_sha(discussion.commit_id) - else - unless discussion.active? an old version of diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 0b23a06f5a9..e6cfd7d56bb 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -26,16 +26,16 @@ %ul - if dashboard_nav_link?(:groups) %li.d-md-none - = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups' do + = link_to dashboard_groups_path, class: 'dashboard-shortcuts-groups', data: { qa_selector: 'groups_link' } do = _('Groups') - if dashboard_nav_link?(:activity) = nav_link(path: 'dashboard#activity') do - = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity' do + = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', data: { qa_selector: 'activity_link' } do = _('Activity') - if dashboard_nav_link?(:milestones) = nav_link(controller: 'dashboard/milestones') do - = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones' do + = link_to dashboard_milestones_path, class: 'dashboard-shortcuts-milestones', data: { qa_selector: 'milestones_link' } do = _('Milestones') - if dashboard_nav_link?(:snippets) diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml index cf7fe36af9d..4b76dde681e 100644 --- a/app/views/projects/diffs/_diffs.html.haml +++ b/app/views/projects/diffs/_diffs.html.haml @@ -16,6 +16,8 @@ = diff_merge_request_whitespace_link(diffs.project, @merge_request, class: 'd-none d-sm-inline-block') - elsif current_controller?(:compare) = diff_compare_whitespace_link(diffs.project, params[:from], params[:to], class: 'd-none d-sm-inline-block') + - elsif current_controller?(:wikis) + = toggle_whitespace_link(url_for(params_with_whitespace), class: 'd-none d-sm-inline-block') .btn-group = inline_diff_btn = parallel_diff_btn diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml index cbb2763550e..1db4554541d 100644 --- a/app/views/projects/wikis/git_access.html.haml +++ b/app/views/projects/wikis/git_access.html.haml @@ -2,8 +2,7 @@ - page_title s_("WikiClone|Git Access"), _("Wiki") .wiki-page-header.top-area.has-sidebar-toggle.py-3.flex-column.flex-lg-row - %button.btn.btn-default.d-block.d-sm-block.d-md-none.float-right.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } - = sprite_icon('chevron-double-lg-left') + = wiki_sidebar_toggle_button .git-access-header.w-100.d-flex.flex-column.justify-content-center %span diff --git a/app/views/shared/wikis/diff.html.haml b/app/views/shared/wikis/diff.html.haml new file mode 100644 index 00000000000..6fce3f5894e --- /dev/null +++ b/app/views/shared/wikis/diff.html.haml @@ -0,0 +1,32 @@ +- wiki_page_title @page, _('Changes') +- commit = @diffs.diffable + +.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row + = wiki_sidebar_toggle_button + + .nav-text + %h2.wiki-page-title + = link_to_wiki_page @page + %span.light + · + = _('Changes') + + .nav-controls.pb-md-3.pb-lg-0 + = link_to wiki_page_path(@wiki, @page, action: :history), class: 'btn', role: 'button', data: { qa_selector: 'page_history_button' } do + = s_('Wiki|Page history') + +.page-content-header + .header-main-content + %strong= markdown_field(commit, :title) + %span.d-none.d-sm-inline= _('authored') + #{time_ago_with_tooltip(commit.authored_date)} + %span= s_('ByAuthor|by') + = author_avatar(commit, size: 24, has_tooltip: false) + %strong + = commit_author_link(commit, avatar: true, size: 24) + - if commit.description.present? + %pre.commit-description< + = preserve(markdown_field(commit, :description)) + += render 'projects/diffs/diffs', diffs: @diffs += render 'shared/wikis/sidebar' diff --git a/app/views/shared/wikis/edit.html.haml b/app/views/shared/wikis/edit.html.haml index 11d0a105c74..64a4816def6 100644 --- a/app/views/shared/wikis/edit.html.haml +++ b/app/views/shared/wikis/edit.html.haml @@ -1,18 +1,14 @@ -- @content_class = "limit-container-width" unless fluid_layout -- add_to_breadcrumbs _("Wiki"), wiki_page_path(@wiki, @page) -- breadcrumb_title @page.persisted? ? _("Edit") : _("New") -- page_title @page.persisted? ? _("Edit") : _("New"), @page.human_title, _("Wiki") +- wiki_page_title @page, @page.persisted? ? _('Edit') : _('New') = wiki_page_errors(@error) .wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row - %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } - = sprite_icon('chevron-double-lg-left') + = wiki_sidebar_toggle_button .nav-text %h2.wiki-page-title - if @page.persisted? - = link_to @page.human_title, wiki_page_path(@wiki, @page) + = link_to_wiki_page @page %span.light · = s_("Wiki|Edit Page") diff --git a/app/views/shared/wikis/history.html.haml b/app/views/shared/wikis/history.html.haml index bceecf2ae24..f9d21c8fb57 100644 --- a/app/views/shared/wikis/history.html.haml +++ b/app/views/shared/wikis/history.html.haml @@ -1,41 +1,38 @@ -- page_title _("History"), @page.human_title, _("Wiki") +- wiki_page_title @page, _('History') .wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row - %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } - = sprite_icon('chevron-double-lg-left') + = wiki_sidebar_toggle_button .nav-text %h2.wiki-page-title - = link_to @page.human_title, wiki_page_path(@wiki, @page) + = link_to_wiki_page @page %span.light · - = _("History") + = _('History') -.table-holder - %table.table - %thead - %tr - %th= s_("Wiki|Page version") - %th= _("Author") - %th= _("Commit Message") - %th= _("Last updated") - %th= _("Format") - %tbody - - @page_versions.each_with_index do |version, index| - - commit = version +.prepend-top-default.gl-mb-3 + .table-holder + %table.table.wiki-history + %thead %tr - %td - = link_to wiki_page_path(@wiki, @page, version_id: index == 0 ? nil : commit.id) do - = truncate_sha(commit.id) - %td - = commit.author_name - %td - = commit.message - %td - #{time_ago_with_tooltip(version.authored_date)} - %td - %strong - = version.format -= paginate @page_versions, theme: 'gitlab' + %th= s_('Wiki|Page version') + %th= _('Author') + %th= _('Changes') + %th= _('Last updated') + %tbody + - @page_versions.each do |commit| + %tr + %td + = link_to wiki_page_path(@wiki, @page, version_id: commit.id) do + = truncate_sha(commit.id) + %td + = commit.author_name + %td + %span.str-truncated-60 + = link_to wiki_page_path(@wiki, @page, action: :diff, version_id: commit.id), { title: commit.message } do + = commit.message + %td + = time_ago_with_tooltip(commit.authored_date) + = paginate @page_versions, theme: 'gitlab' = render 'shared/wikis/sidebar' diff --git a/app/views/shared/wikis/show.html.haml b/app/views/shared/wikis/show.html.haml index 365afbf511d..9f016647690 100644 --- a/app/views/shared/wikis/show.html.haml +++ b/app/views/shared/wikis/show.html.haml @@ -1,19 +1,14 @@ -- @content_class = "limit-container-width" unless fluid_layout -- breadcrumb_title @page.human_title -- wiki_breadcrumb_dropdown_links(@page.slug) -- page_title @page.human_title, _("Wiki") -- add_to_breadcrumbs _("Wiki"), wiki_path(@wiki) +- wiki_page_title @page .wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row - %button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" } - = sprite_icon('chevron-double-lg-left') + = wiki_sidebar_toggle_button .nav-text.flex-fill %h2.wiki-page-title{ data: { qa_selector: 'wiki_page_title' } }= @page.human_title %span.wiki-last-edit-by - if @page.last_version = (_("Last edited by %{name}") % { name: "#{@page.last_version.author_name}" }).html_safe - #{time_ago_with_tooltip(@page.last_version.authored_date)} + = time_ago_with_tooltip(@page.last_version.authored_date) .nav-controls.pb-md-3.pb-lg-0 = render 'shared/wikis/main_links' diff --git a/changelogs/unreleased/15242-wiki-diff.yml b/changelogs/unreleased/15242-wiki-diff.yml new file mode 100644 index 00000000000..1c9891cdcee --- /dev/null +++ b/changelogs/unreleased/15242-wiki-diff.yml @@ -0,0 +1,5 @@ +--- +title: Allow diffing changes in wiki history +merge_request: 35330 +author: gwhyte, Steve Mokris +type: added diff --git a/changelogs/unreleased/213983-use-version-instead-of-shortversion-for-sentry.yml b/changelogs/unreleased/213983-use-version-instead-of-shortversion-for-sentry.yml new file mode 100644 index 00000000000..b883dcbbf98 --- /dev/null +++ b/changelogs/unreleased/213983-use-version-instead-of-shortversion-for-sentry.yml @@ -0,0 +1,5 @@ +--- +title: Use full version instead of short version for Sentry Error Release links. +merge_request: 35623 +author: +type: fixed diff --git a/changelogs/unreleased/35261-custom-renderer-embedded-ruby.yml b/changelogs/unreleased/35261-custom-renderer-embedded-ruby.yml new file mode 100644 index 00000000000..b3752568909 --- /dev/null +++ b/changelogs/unreleased/35261-custom-renderer-embedded-ruby.yml @@ -0,0 +1,5 @@ +--- +title: Add a custom HTML renderer to the Static Site Editor for embedded ruby (ERB) syntax +merge_request: 35261 +author: +type: added diff --git a/changelogs/unreleased/35775-inline-code-fix-for-custom-renderer-identifiers.yml b/changelogs/unreleased/35775-inline-code-fix-for-custom-renderer-identifiers.yml new file mode 100644 index 00000000000..f343d2727cc --- /dev/null +++ b/changelogs/unreleased/35775-inline-code-fix-for-custom-renderer-identifiers.yml @@ -0,0 +1,5 @@ +--- +title: Fix unique case where static site editor's custom renderer for identifier syntax didn't robustly handle inline code +merge_request: 35775 +author: Derek Knox +type: fixed diff --git a/changelogs/unreleased/ajk-gql-diff-stats-file-count.yml b/changelogs/unreleased/ajk-gql-diff-stats-file-count.yml new file mode 100644 index 00000000000..a5d1c4b0169 --- /dev/null +++ b/changelogs/unreleased/ajk-gql-diff-stats-file-count.yml @@ -0,0 +1,5 @@ +--- +title: Add MergeRequest.diffStatsSummary.fileCount to graphql API +merge_request: 35685 +author: +type: added diff --git a/changelogs/unreleased/justin_ho-enable-integration_form_refactor-by-default.yml b/changelogs/unreleased/justin_ho-enable-integration_form_refactor-by-default.yml new file mode 100644 index 00000000000..d6160ef03ff --- /dev/null +++ b/changelogs/unreleased/justin_ho-enable-integration_form_refactor-by-default.yml @@ -0,0 +1,5 @@ +--- +title: Update integration form to use GitLab UI components +merge_request: 35582 +author: +type: changed diff --git a/changelogs/unreleased/rm-25228-pre-receive-error.yml b/changelogs/unreleased/rm-25228-pre-receive-error.yml new file mode 100644 index 00000000000..da34b3edef5 --- /dev/null +++ b/changelogs/unreleased/rm-25228-pre-receive-error.yml @@ -0,0 +1,5 @@ +--- +title: Propagate error on FF pre-receive failure +merge_request: 35633 +author: +type: fixed diff --git a/config/routes/wiki.rb b/config/routes/wiki.rb index d439c99270e..002b340edee 100644 --- a/config/routes/wiki.rb +++ b/config/routes/wiki.rb @@ -10,6 +10,7 @@ scope(controller: :wikis) do scope(path: 'wikis/*id', as: :wiki, format: false) do get :edit get :history + get :diff post :preview_markdown get '/', action: :show put '/', action: :update diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 2429a0da8c6..70aab7a8c4c 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -3164,6 +3164,11 @@ type DiffStatsSummary { Number of lines deleted """ deletions: Int! + + """ + Number of files changed + """ + fileCount: Int! } type Discussion implements ResolvableInterface { @@ -5393,6 +5398,31 @@ type Group { startDate: ISO8601Date! ): VulnerabilitiesCountByDayAndSeverityConnection + """ + Vulnerability scanners reported on the project vulnerabilties of the group and its subgroups + """ + vulnerabilityScanners( + """ + Returns the elements in the list that come after the specified cursor. + """ + after: String + + """ + Returns the elements in the list that come before the specified cursor. + """ + before: String + + """ + Returns the first _n_ elements from the list. + """ + first: Int + + """ + Returns the last _n_ elements from the list. + """ + last: Int + ): VulnerabilityScannerConnection + """ Web URL of the group """ @@ -5520,6 +5550,31 @@ type InstanceSecurityDashboard { """ last: Int ): ProjectConnection! + + """ + Vulnerability scanners reported on the vulnerabilties from projects selected in Instance Security Dashboard + """ + vulnerabilityScanners( + """ + Returns the elements in the list that come after the specified cursor. + """ + after: String + + """ + Returns the elements in the list that come before the specified cursor. + """ + before: String + + """ + Returns the first _n_ elements from the list. + """ + first: Int + + """ + Returns the last _n_ elements from the list. + """ + last: Int + ): VulnerabilityScannerConnection } """ @@ -9771,6 +9826,31 @@ type Project { state: [VulnerabilityState!] ): VulnerabilityConnection + """ + Vulnerability scanners reported on the project vulnerabilties + """ + vulnerabilityScanners( + """ + Returns the elements in the list that come after the specified cursor. + """ + after: String + + """ + Returns the elements in the list that come before the specified cursor. + """ + before: String + + """ + Returns the first _n_ elements from the list. + """ + first: Int + + """ + Returns the last _n_ elements from the list. + """ + last: Int + ): VulnerabilityScannerConnection + """ Counts for each severity of vulnerability of the project """ @@ -11465,10 +11545,15 @@ type SentryDetailedError { firstReleaseLastCommit: String """ - Release version the error was first seen + Release short version the error was first seen """ firstReleaseShortVersion: String + """ + Release version the error was first seen + """ + firstReleaseVersion: String + """ Timestamp when the error was first seen """ @@ -11505,10 +11590,15 @@ type SentryDetailedError { lastReleaseLastCommit: String """ - Release version the error was last seen + Release short version the error was last seen """ lastReleaseShortVersion: String + """ + Release version the error was last seen + """ + lastReleaseVersion: String + """ Timestamp when the error was last seen """ @@ -14443,6 +14533,51 @@ type VulnerabilityScanner { Name of the vulnerability scanner """ name: String + + """ + Type of the vulnerability report + """ + reportType: VulnerabilityReportType + + """ + Vendor of the vulnerability scanner + """ + vendor: String +} + +""" +The connection type for VulnerabilityScanner. +""" +type VulnerabilityScannerConnection { + """ + A list of edges. + """ + edges: [VulnerabilityScannerEdge] + + """ + A list of nodes. + """ + nodes: [VulnerabilityScanner] + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An edge in a connection. +""" +type VulnerabilityScannerEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of the edge. + """ + node: VulnerabilityScanner } """ diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index 758be106669..a596baa38d8 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -8778,6 +8778,24 @@ "description": "Number of lines deleted", "args": [ + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "fileCount", + "description": "Number of files changed", + "args": [ + ], "type": { "kind": "NON_NULL", @@ -14808,6 +14826,59 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "vulnerabilityScanners", + "description": "Vulnerability scanners reported on the project vulnerabilties of the group and its subgroups", + "args": [ + { + "name": "after", + "description": "Returns the elements in the list that come after the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "before", + "description": "Returns the elements in the list that come before the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "first", + "description": "Returns the first _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "last", + "description": "Returns the last _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "VulnerabilityScannerConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "webUrl", "description": "Web URL of the group", @@ -15206,6 +15277,59 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "vulnerabilityScanners", + "description": "Vulnerability scanners reported on the vulnerabilties from projects selected in Instance Security Dashboard", + "args": [ + { + "name": "after", + "description": "Returns the elements in the list that come after the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "before", + "description": "Returns the elements in the list that come before the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "first", + "description": "Returns the first _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "last", + "description": "Returns the last _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "VulnerabilityScannerConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, @@ -28685,6 +28809,59 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "vulnerabilityScanners", + "description": "Vulnerability scanners reported on the project vulnerabilties", + "args": [ + { + "name": "after", + "description": "Returns the elements in the list that come after the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "before", + "description": "Returns the elements in the list that come before the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "first", + "description": "Returns the first _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "last", + "description": "Returns the last _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "VulnerabilityScannerConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "vulnerabilitySeveritiesCount", "description": "Counts for each severity of vulnerability of the project", @@ -33654,6 +33831,20 @@ }, { "name": "firstReleaseShortVersion", + "description": "Release short version the error was first seen", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "firstReleaseVersion", "description": "Release version the error was first seen", "args": [ @@ -33786,6 +33977,20 @@ }, { "name": "lastReleaseShortVersion", + "description": "Release short version the error was last seen", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "lastReleaseVersion", "description": "Release version the error was last seen", "args": [ @@ -42503,6 +42708,146 @@ }, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "reportType", + "description": "Type of the vulnerability report", + "args": [ + + ], + "type": { + "kind": "ENUM", + "name": "VulnerabilityReportType", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "vendor", + "description": "Vendor of the vulnerability scanner", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "VulnerabilityScannerConnection", + "description": "The connection type for VulnerabilityScanner.", + "fields": [ + { + "name": "edges", + "description": "A list of edges.", + "args": [ + + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "VulnerabilityScannerEdge", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "nodes", + "description": "A list of nodes.", + "args": [ + + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "VulnerabilityScanner", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "pageInfo", + "description": "Information to aid in pagination.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "PageInfo", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "VulnerabilityScannerEdge", + "description": "An edge in a connection.", + "fields": [ + { + "name": "cursor", + "description": "A cursor for use in pagination.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "node", + "description": "The item at the end of the edge.", + "args": [ + + ], + "type": { + "kind": "OBJECT", + "name": "VulnerabilityScanner", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null } ], "inputFields": null, diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 5e1eafa0645..57f37052c83 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -545,6 +545,7 @@ Aggregated summary of changes | `additions` | Int! | Number of lines added | | `changes` | Int! | Number of lines changed | | `deletions` | Int! | Number of lines deleted | +| `fileCount` | Int! | Number of files changed | ## Discussion @@ -1679,7 +1680,8 @@ A Sentry error. | `externalBaseUrl` | String! | External Base URL of the Sentry Instance | | `externalUrl` | String! | External URL of the error | | `firstReleaseLastCommit` | String | Commit the error was first seen | -| `firstReleaseShortVersion` | String | Release version the error was first seen | +| `firstReleaseShortVersion` | String | Release short version the error was first seen | +| `firstReleaseVersion` | String | Release version the error was first seen | | `firstSeen` | Time! | Timestamp when the error was first seen | | `frequency` | SentryErrorFrequency! => Array | Last 24hr stats of the error | | `gitlabCommit` | String | GitLab commit SHA attributed to the Error based on the release version | @@ -1687,7 +1689,8 @@ A Sentry error. | `gitlabIssuePath` | String | URL of GitLab Issue | | `id` | ID! | ID (global ID) of the error | | `lastReleaseLastCommit` | String | Commit the error was last seen | -| `lastReleaseShortVersion` | String | Release version the error was last seen | +| `lastReleaseShortVersion` | String | Release short version the error was last seen | +| `lastReleaseVersion` | String | Release version the error was last seen | | `lastSeen` | Time! | Timestamp when the error was last seen | | `message` | String | Sentry metadata message of the error | | `sentryId` | String! | ID (Sentry ID) of the error | @@ -2219,6 +2222,8 @@ Represents a vulnerability scanner. | --- | ---- | ---------- | | `externalId` | String | External ID of the vulnerability scanner | | `name` | String | Name of the vulnerability scanner | +| `reportType` | VulnerabilityReportType | Type of the vulnerability report | +| `vendor` | String | Vendor of the vulnerability scanner | ## VulnerabilitySeveritiesCount diff --git a/doc/api/members.md b/doc/api/members.md index 979d8a7a186..09eb0def811 100644 --- a/doc/api/members.md +++ b/doc/api/members.md @@ -290,7 +290,7 @@ Example response: ### Set override flag for a member of a group -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4875) in GitLab 12.10. +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4875) in GitLab 13.0. By default, the access level of LDAP group members is set to the value specified by LDAP through Group Sync. You can allow access level overrides by calling this endpoint. @@ -326,7 +326,7 @@ Example response: ### Remove override for a member of a group -> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4875) in GitLab 12.10. +> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/4875) in GitLab 13.0. Sets the override flag to false and allows LDAP Group Sync to reset the access level to the LDAP-prescribed value. diff --git a/doc/development/fe_guide/index.md b/doc/development/fe_guide/index.md index 8fd6ba459b9..3a2b3cac9bf 100644 --- a/doc/development/fe_guide/index.md +++ b/doc/development/fe_guide/index.md @@ -17,7 +17,9 @@ Working with our frontend assets requires Node (v10.13.0 or greater) and Yarn For our currently-supported browsers, see our [requirements](../../install/requirements.md#supported-web-browsers). -Use [BrowserStack](https://www.browserstack.com/) to test with our supported browsers. Login to BrowserStack with the credentials saved in GitLab's [shared 1Password account](https://about.gitlab.com/handbook/security/#1password-for-teams). +Use [BrowserStack](https://www.browserstack.com/) to test with our supported browsers. +Sign in to BrowserStack with the credentials saved in the **Engineering** vault of GitLab's +[shared 1Password account](https://about.gitlab.com/handbook/security/#1password-guide). ## Initiatives diff --git a/doc/development/feature_flags/process.md b/doc/development/feature_flags/process.md index 5cc08de159c..8ccaa473be6 100644 --- a/doc/development/feature_flags/process.md +++ b/doc/development/feature_flags/process.md @@ -88,7 +88,7 @@ Take into consideration that such action can make the feature available on GitLab.com shortly after the change to the feature flag is merged. Changing the default state or removing the feature flag has to be done before -the 22nd of the month, _at least_ 2 working days before, in order for the change +the 22nd of the month, _at least_ 3-4 working days before, in order for the change to be included in the final self-managed release. In addition to this, the feature behind feature flag should: diff --git a/doc/development/testing_guide/frontend_testing.md b/doc/development/testing_guide/frontend_testing.md index 37e1066e7aa..02ee627af15 100644 --- a/doc/development/testing_guide/frontend_testing.md +++ b/doc/development/testing_guide/frontend_testing.md @@ -856,7 +856,8 @@ Some regressions only affect a specific browser version. We can install and test [BrowserStack](https://www.browserstack.com/) allows you to test more than 1200 mobile devices and browsers. You can use it directly through the [live app](https://www.browserstack.com/live) or you can install the [chrome extension](https://chrome.google.com/webstore/detail/browserstack/nkihdmlheodkdfojglpcjjmioefjahjb) for easy access. -You can find the credentials on 1Password, under `frontendteam@gitlab.com`. +Sign in to BrowserStack with the credentials saved in the **Engineering** vault of GitLab's +[shared 1Password account](https://about.gitlab.com/handbook/security/#1password-guide). ### Firefox diff --git a/doc/user/project/integrations/img/heatmap_chart_too_much_data_v_13_2.png b/doc/user/project/integrations/img/heatmap_chart_too_much_data_v_13_2.png new file mode 100755 index 00000000000..c3a391b06c7 Binary files /dev/null and b/doc/user/project/integrations/img/heatmap_chart_too_much_data_v_13_2.png differ diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index e781493bd85..337f9461e9a 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -748,6 +748,11 @@ Note the following properties: ![heatmap panel type](img/heatmap_panel_type.png) +CAUTION: **Warning:** +When a query returns too many data points, the heatmap data bucket dimensions tend downwards to 0, making the chart's data invisible, as shown in the image below. To fix this problem, limit the amount of data returned by changing the time range filter on the metrics dashboard UI, or adding the **step** property to your dashboard's YAML file. + +![heatmap chart_too_much_data](img/heatmap_chart_too_much_data_v_13_2.png) + ### Templating variables for metrics dashboards Templating variables can be used to make your metrics dashboard more versatile. diff --git a/lib/gitlab/danger/helper.rb b/lib/gitlab/danger/helper.rb index 327418ad100..58f9d1196cf 100644 --- a/lib/gitlab/danger/helper.rb +++ b/lib/gitlab/danger/helper.rb @@ -73,16 +73,16 @@ module Gitlab # @return [Hash>] def changes_by_category all_changed_files.each_with_object(Hash.new { |h, k| h[k] = [] }) do |file, hash| - hash[category_for_file(file)] << file + categories_for_file(file).each { |category| hash[category] << file } end end - # Determines the category a file is in, e.g., `:frontend` or `:backend` - # @return[Symbol] - def category_for_file(file) - _, category = CATEGORIES.find { |regexp, _| regexp.match?(file) } + # Determines the categories a file is in, e.g., `[:frontend]`, `[:backend]`, or `%i[frontend engineering_productivity]`. + # @return Array + def categories_for_file(file) + _, categories = CATEGORIES.find { |regexp, _| regexp.match?(file) } - category || :unknown + Array(categories || :unknown) end # Returns the GFM for a category label, making its best guess if it's not @@ -125,10 +125,13 @@ module Gitlab jest\.config\.js | package\.json | yarn\.lock | - config/.+\.js | - \.gitlab/ci/frontend\.gitlab-ci\.yml + config/.+\.js )\z}x => :frontend, + %r{(\A|/)( + \.gitlab/ci/frontend\.gitlab-ci\.yml + )\z}x => %i[frontend engineering_productivity], + %r{\A(ee/)?db/(?!fixtures)[^/]+} => :database, %r{\A(ee/)?lib/gitlab/(database|background_migration|sql|github_import)(/|\.rb)} => :database, %r{\A(app/models/project_authorization|app/services/users/refresh_authorized_projects_service)(/|\.rb)} => :database, diff --git a/lib/gitlab/diff/file_collection/wiki_page.rb b/lib/gitlab/diff/file_collection/wiki_page.rb new file mode 100644 index 00000000000..7873e85a0eb --- /dev/null +++ b/lib/gitlab/diff/file_collection/wiki_page.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Gitlab + module Diff + module FileCollection + class WikiPage < Base + def initialize(page, diff_options:) + commit = page.wiki.commit(page.version.commit) + diff_options = diff_options.merge( + expanded: true, + paths: [page.path] + ) + + super(commit, + # TODO: Uncouple diffing from projects + # https://gitlab.com/gitlab-org/gitlab/-/issues/217752 + project: page.wiki, + diff_options: diff_options, + diff_refs: commit.diff_refs) + end + end + end + end +end diff --git a/lib/gitlab/error_tracking/detailed_error.rb b/lib/gitlab/error_tracking/detailed_error.rb index b49f2472e01..5d272efa64a 100644 --- a/lib/gitlab/error_tracking/detailed_error.rb +++ b/lib/gitlab/error_tracking/detailed_error.rb @@ -22,6 +22,7 @@ module Gitlab :id, :last_release_last_commit, :last_release_short_version, + :last_release_version, :last_seen, :message, :project_id, diff --git a/lib/gitlab/git/wiki.rb b/lib/gitlab/git/wiki.rb index 3025fc6bfdb..76771f0417b 100644 --- a/lib/gitlab/git/wiki.rb +++ b/lib/gitlab/git/wiki.rb @@ -101,6 +101,10 @@ module Gitlab wrapped_gitaly_errors do gitaly_find_page(title: title, version: version, dir: dir) end + rescue Gitlab::Git::CommandError + # Return nil for invalid versions. + # This can be removed with https://gitlab.com/gitlab-org/gitaly/-/merge_requests/2323 in place. + nil end def file(name, version) diff --git a/lib/gitlab/gitaly_client/operation_service.rb b/lib/gitlab/gitaly_client/operation_service.rb index 9ed4b2da09a..87505418ae9 100644 --- a/lib/gitlab/gitaly_client/operation_service.rb +++ b/lib/gitlab/gitaly_client/operation_service.rb @@ -178,6 +178,10 @@ module Gitlab timeout: GitalyClient.long_timeout ) + if response.pre_receive_error.present? + raise Gitlab::Git::PreReceiveError.new("GL-HOOK-ERR: pre-receive hook failed.") + end + Gitlab::Git::OperationService::BranchUpdate.from_gitaly(response.branch_update) rescue GRPC::FailedPrecondition => e raise Gitlab::Git::CommitError, e diff --git a/lib/sentry/client/issue.rb b/lib/sentry/client/issue.rb index 4a62b73a349..c5e9df9cd21 100644 --- a/lib/sentry/client/issue.rb +++ b/lib/sentry/client/issue.rb @@ -168,7 +168,8 @@ module Sentry first_release_short_version: issue.dig('firstRelease', 'shortVersion'), first_release_version: issue.dig('firstRelease', 'version'), last_release_last_commit: issue.dig('lastRelease', 'lastCommit'), - last_release_short_version: issue.dig('lastRelease', 'shortVersion') + last_release_short_version: issue.dig('lastRelease', 'shortVersion'), + last_release_version: issue.dig('lastRelease', 'version') }) end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 5d081cc1266..2a49b745429 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -10187,9 +10187,6 @@ msgstr "" msgid "Forks" msgstr "" -msgid "Format" -msgstr "" - msgid "Format: %{dateFormat}" msgstr "" @@ -10280,6 +10277,9 @@ msgstr "" msgid "Generate new export" msgstr "" +msgid "GenericReports|Report" +msgstr "" + msgid "Geo" msgstr "" @@ -11234,16 +11234,16 @@ msgstr "" msgid "Group: %{name}" msgstr "" -msgid "GroupActivityMetrics|New Members created" +msgid "GroupActivityMetrics|Issues opened" msgstr "" -msgid "GroupActivyMetrics|Issues created" +msgid "GroupActivityMetrics|Members added" msgstr "" -msgid "GroupActivyMetrics|Merge Requests created" +msgid "GroupActivityMetrics|Merge Requests opened" msgstr "" -msgid "GroupActivyMetrics|Recent activity (last 90 days)" +msgid "GroupActivityMetrics|Recent activity (last 90 days)" msgstr "" msgid "GroupImport|Failed to import group." @@ -15311,9 +15311,6 @@ msgstr "" msgid "No test coverage" msgstr "" -msgid "No thanks" -msgstr "" - msgid "No vulnerabilities present" msgstr "" @@ -25306,6 +25303,9 @@ msgstr "" msgid "View open merge request" msgstr "" +msgid "View page @ " +msgstr "" + msgid "View performance dashboard." msgstr "" @@ -27551,7 +27551,7 @@ msgstr "" msgid "mrWidget|To approve this merge request, please enter your password. This project requires all approvals to be authenticated." msgstr "" -msgid "mrWidget|Use %{linkStart}CI pipelines to test your code%{linkEnd}, simply add a GitLab CI configuration file to your project. It only takes a minute to make your code more secure and robust." +msgid "mrWidget|Use %{linkStart}CI pipelines to test your code%{linkEnd} by simply adding a GitLab CI configuration file to your project. It only takes a minute to make your code more secure and robust." msgstr "" msgid "mrWidget|When this merge request is ready, remove the WIP: prefix from the title to allow it to be merged" diff --git a/qa/qa.rb b/qa/qa.rb index baab9b577a3..3f93d79ee51 100644 --- a/qa/qa.rb +++ b/qa/qa.rb @@ -448,6 +448,7 @@ module QA autoload :ConfirmModal, 'qa/page/component/confirm_modal' autoload :CustomMetric, 'qa/page/component/custom_metric' autoload :DesignManagement, 'qa/page/component/design_management' + autoload :ProjectSelector, 'qa/page/component/project_selector' module Issuable autoload :Common, 'qa/page/component/issuable/common' diff --git a/qa/qa/page/component/project_selector.rb b/qa/qa/page/component/project_selector.rb new file mode 100644 index 00000000000..80ed6b8e53b --- /dev/null +++ b/qa/qa/page/component/project_selector.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +module QA + module Page + module Component + module ProjectSelector + extend QA::Page::PageConcern + + def self.included(base) + super + + base.view 'app/assets/javascripts/vue_shared/components/project_selector/project_selector.vue' do + element :project_search_field + element :project_list_item + end + end + + def fill_project_search_input(project_name) + fill_element :project_search_field, project_name + end + + def select_project + click_element :project_list_item + end + end + end + end +end diff --git a/qa/qa/page/main/menu.rb b/qa/qa/page/main/menu.rb index 6af18cb1d2b..416946f44f0 100644 --- a/qa/qa/page/main/menu.rb +++ b/qa/qa/page/main/menu.rb @@ -22,6 +22,9 @@ module QA element :groups_dropdown, required: true element :more_dropdown element :snippets_link + element :groups_link + element :activity_link + element :milestones_link end view 'app/views/layouts/nav/projects_dropdown/_show.html.haml' do @@ -53,10 +56,10 @@ module QA end end - def go_to_snippets + def go_to_more_dropdown_option(option_name) within_top_menu do click_element :more_dropdown - click_element :snippets_link + click_element option_name end end @@ -148,3 +151,5 @@ module QA end end end + +QA::Page::Main::Menu.prepend_if_ee('QA::EE::Page::Main::Menu') diff --git a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb index 451a7847f8b..b7e9be8e326 100644 --- a/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/snippet/create_personal_snippet_spec.rb @@ -6,7 +6,9 @@ module QA it 'User creates a personal snippet' do Flow::Login.sign_in - Page::Main::Menu.perform(&:go_to_snippets) + Page::Main::Menu.perform do |menu| + menu.go_to_more_dropdown_option(:snippets_link) + end Resource::Snippet.fabricate_via_browser_ui! do |snippet| snippet.title = 'Snippet title' diff --git a/rubocop/cop/graphql/authorize_types.rb b/rubocop/cop/graphql/authorize_types.rb index c6dbe447b4a..1dba719cdff 100644 --- a/rubocop/cop/graphql/authorize_types.rb +++ b/rubocop/cop/graphql/authorize_types.rb @@ -8,29 +8,29 @@ module RuboCop 'https://docs.gitlab.com/ee/development/api_graphql_styleguide.html#type-authorization' # We want to exclude our own basetypes and scalars - WHITELISTED_TYPES = %w[BaseEnum BaseScalar BasePermissionType MutationType - QueryType GraphQL::Schema BaseUnion].freeze + ALLOWED_TYPES = %w[BaseEnum BaseScalar BasePermissionType MutationType + QueryType GraphQL::Schema BaseUnion].freeze def_node_search :authorize?, <<~PATTERN (send nil? :authorize ...) PATTERN def on_class(node) - return if whitelisted?(class_constant(node)) - return if whitelisted?(superclass_constant(node)) + return if allowed?(class_constant(node)) + return if allowed?(superclass_constant(node)) add_offense(node, location: :expression) unless authorize?(node) end private - def whitelisted?(class_node) + def allowed?(class_node) class_const = class_node&.const_name return false unless class_const return true if class_const.end_with?('Enum') - WHITELISTED_TYPES.any? { |whitelisted| class_node.const_name.include?(whitelisted) } + ALLOWED_TYPES.any? { |allowed| class_node.const_name.include?(allowed) } end def class_constant(node) diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb index 59ccb83a9bb..e93689af0aa 100644 --- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb @@ -8,9 +8,10 @@ RSpec.describe 'User views a wiki page' do let(:user) { create(:user) } let(:project) { create(:project, :wiki_repo, namespace: user.namespace) } let(:path) { 'image.png' } + let(:wiki) { project.wiki } let(:wiki_page) do create(:wiki_page, - wiki: project.wiki, + wiki: wiki, title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})") end @@ -70,11 +71,13 @@ RSpec.describe 'User views a wiki page' do click_on('Page history') - page.within(:css, '.nav-text') do + within('.nav-text') do expect(page).to have_content('History') end - find('a[href*="?version_id"]') + within('.wiki-history') do + expect(page).to have_css('a[href*="?version_id"]', count: 4) + end end end @@ -92,8 +95,8 @@ RSpec.describe 'User views a wiki page' do let(:path) { upload_file_to_wiki(project, user, 'dk.png') } it do - expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/#{path}']") - expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}") + expect(page).to have_xpath("//img[@data-src='#{wiki.wiki_base_path}/#{path}']") + expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/#{path}") click_on('image') @@ -103,7 +106,7 @@ RSpec.describe 'User views a wiki page' do end it 'shows the creation page if file does not exist' do - expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}") + expect(page).to have_link('image', href: "#{wiki.wiki_base_path}/#{path}") click_on('image') @@ -114,7 +117,7 @@ RSpec.describe 'User views a wiki page' do context 'when a page has history' do before do - wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)') + wiki_page.update(message: 'updated home', content: 'updated [some link](other-page)') # rubocop:disable Rails/SaveBang end it 'shows the page history' do @@ -134,13 +137,74 @@ RSpec.describe 'User views a wiki page' do expect(page).not_to have_selector('a.btn', text: 'Edit') end + + context 'show the diff' do + def expect_diff_links(commit) + diff_path = wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff) + + expect(page).to have_link('Hide whitespace changes', href: "#{diff_path}&w=1") + expect(page).to have_link('Inline', href: "#{diff_path}&view=inline") + expect(page).to have_link('Side-by-side', href: "#{diff_path}&view=parallel") + expect(page).to have_link("View page @ #{commit.short_id}", href: wiki_page_path(wiki, wiki_page, version_id: commit)) + expect(page).to have_css('.diff-file[data-blob-diff-path="%s"]' % diff_path) + end + + it 'links to the correct diffs' do + visit project_wiki_history_path(project, wiki_page) + + commit1 = wiki.commit('HEAD^') + commit2 = wiki.commit + + expect(page).to have_link('created page: home', href: wiki_page_path(wiki, wiki_page, version_id: commit1, action: :diff)) + expect(page).to have_link('updated home', href: wiki_page_path(wiki, wiki_page, version_id: commit2, action: :diff)) + end + + it 'between the current and the previous version of a page' do + commit = wiki.commit + visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff) + + expect(page).to have_content('by John Doe') + expect(page).to have_content('updated home') + expect(page).to have_content('Showing 1 changed file with 1 addition and 3 deletions') + expect(page).to have_content('some link') + + expect_diff_links(commit) + end + + it 'between two old versions of a page' do + wiki_page.update(message: 'latest home change', content: 'updated [another link](other-page)') # rubocop:disable Rails/SaveBang: + commit = wiki.commit('HEAD^') + visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff) + + expect(page).to have_content('by John Doe') + expect(page).to have_content('updated home') + expect(page).to have_content('Showing 1 changed file with 1 addition and 3 deletions') + expect(page).to have_content('some link') + expect(page).not_to have_content('latest home change') + expect(page).not_to have_content('another link') + + expect_diff_links(commit) + end + + it 'for the oldest version of a page' do + commit = wiki.commit('HEAD^') + visit wiki_page_path(wiki, wiki_page, version_id: commit, action: :diff) + + expect(page).to have_content('by John Doe') + expect(page).to have_content('created page: home') + expect(page).to have_content('Showing 1 changed file with 4 additions and 0 deletions') + expect(page).to have_content('Look at this') + + expect_diff_links(commit) + end + end end context 'when a page has special characters in its title' do let(:title) { ' !@#$%^&*()[]{}=_+\'"\\|<>? ' } before do - wiki_page.update(title: title ) + wiki_page.update(title: title ) # rubocop:disable Rails/SaveBang end it 'preserves the special characters' do @@ -155,7 +219,7 @@ RSpec.describe 'User views a wiki page' do let(:title) { ' bar') + wiki_page.update(title: title, content: 'foo bar') # rubocop:disable Rails/SaveBang end it 'safely displays the page' do @@ -168,7 +232,7 @@ RSpec.describe 'User views a wiki page' do context 'when a page has XSS in its message' do before do - wiki_page.update(message: '