diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-02 12:10:23 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-09-02 12:10:23 +0300 |
commit | 4b9ace6c1fead1b44f173eaee0cfaa58f46a258a (patch) | |
tree | a411c934419690755623a57ff7ea5f47050050e2 /app | |
parent | 03a521732276f8abc4ba069dd985b22cd9bc5929 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
28 files changed, 299 insertions, 100 deletions
diff --git a/app/assets/javascripts/diffs/components/settings_dropdown.vue b/app/assets/javascripts/diffs/components/settings_dropdown.vue index 80b44f7bb13..78647065c8e 100644 --- a/app/assets/javascripts/diffs/components/settings_dropdown.vue +++ b/app/assets/javascripts/diffs/components/settings_dropdown.vue @@ -1,16 +1,24 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; -import { GlDeprecatedButton, GlIcon } from '@gitlab/ui'; +import { GlButtonGroup, GlButton, GlDropdown } from '@gitlab/ui'; +import { __ } from '~/locale'; export default { components: { - GlDeprecatedButton, - GlIcon, + GlButtonGroup, + GlButton, + GlDropdown, }, computed: { ...mapGetters('diffs', ['isInlineView', 'isParallelView']), ...mapState('diffs', ['renderTreeList', 'showWhitespace']), }, + mounted() { + this.patchAriaLabel(); + }, + updated() { + this.patchAriaLabel(); + }, methods: { ...mapActions('diffs', [ 'setInlineDiffViewType', @@ -18,74 +26,69 @@ export default { 'setRenderTreeList', 'setShowWhitespace', ]), + patchAriaLabel() { + this.$el + .querySelector('.js-show-diff-settings') + .setAttribute('aria-label', __('Diff view settings')); + }, }, }; </script> <template> - <div class="dropdown"> - <button - type="button" - class="btn btn-default js-show-diff-settings" - data-toggle="dropdown" - data-display="static" - > - <gl-icon name="settings" /> <gl-icon name="chevron-down" /> - </button> - <div class="dropdown-menu dropdown-menu-right p-2 pt-3 pb-3"> - <div> - <span class="bold d-block mb-1">{{ __('File browser') }}</span> - <div class="btn-group d-flex"> - <gl-deprecated-button - :class="{ active: !renderTreeList }" - class="w-100 js-list-view" - @click="setRenderTreeList(false)" - > - {{ __('List view') }} - </gl-deprecated-button> - <gl-deprecated-button - :class="{ active: renderTreeList }" - class="w-100 js-tree-view" - @click="setRenderTreeList(true)" - > - {{ __('Tree view') }} - </gl-deprecated-button> - </div> - </div> - <div class="mt-2"> - <span class="bold d-block mb-1">{{ __('Compare changes') }}</span> - <div class="btn-group d-flex js-diff-view-buttons"> - <gl-deprecated-button - id="inline-diff-btn" - :class="{ active: isInlineView }" - class="w-100 js-inline-diff-button" - data-view-type="inline" - @click="setInlineDiffViewType" - > - {{ __('Inline') }} - </gl-deprecated-button> - <gl-deprecated-button - id="parallel-diff-btn" - :class="{ active: isParallelView }" - class="w-100 js-parallel-diff-button" - data-view-type="parallel" - @click="setParallelDiffViewType" - > - {{ __('Side-by-side') }} - </gl-deprecated-button> - </div> - </div> - <div class="mt-2"> - <label class="mb-0"> - <input - id="show-whitespace" - type="checkbox" - :checked="showWhitespace" - @change="setShowWhitespace({ showWhitespace: $event.target.checked, pushState: true })" - /> - {{ __('Show whitespace changes') }} - </label> - </div> + <gl-dropdown icon="settings" toggle-class="js-show-diff-settings" right> + <div class="gl-px-3"> + <span class="gl-font-weight-bold gl-display-block gl-mb-2">{{ __('File browser') }}</span> + <gl-button-group class="gl-display-flex"> + <gl-button + :class="{ selected: !renderTreeList }" + class="gl-w-half js-list-view" + @click="setRenderTreeList(false)" + > + {{ __('List view') }} + </gl-button> + <gl-button + :class="{ selected: renderTreeList }" + class="gl-w-half js-tree-view" + @click="setRenderTreeList(true)" + > + {{ __('Tree view') }} + </gl-button> + </gl-button-group> + </div> + <div class="gl-mt-3 gl-px-3"> + <span class="gl-font-weight-bold gl-display-block gl-mb-2">{{ __('Compare changes') }}</span> + <gl-button-group class="gl-display-flex js-diff-view-buttons"> + <gl-button + id="inline-diff-btn" + :class="{ selected: isInlineView }" + class="gl-w-half js-inline-diff-button" + data-view-type="inline" + @click="setInlineDiffViewType" + > + {{ __('Inline') }} + </gl-button> + <gl-button + id="parallel-diff-btn" + :class="{ selected: isParallelView }" + class="gl-w-half js-parallel-diff-button" + data-view-type="parallel" + @click="setParallelDiffViewType" + > + {{ __('Side-by-side') }} + </gl-button> + </gl-button-group> + </div> + <div class="gl-mt-3 gl-px-3"> + <label class="gl-mb-0"> + <input + id="show-whitespace" + type="checkbox" + :checked="showWhitespace" + @change="setShowWhitespace({ showWhitespace: $event.target.checked, pushState: true })" + /> + {{ __('Show whitespace changes') }} + </label> </div> - </div> + </gl-dropdown> </template> diff --git a/app/assets/javascripts/issue_show/components/app.vue b/app/assets/javascripts/issue_show/components/app.vue index 992d87a969f..1cc04003aa6 100644 --- a/app/assets/javascripts/issue_show/components/app.vue +++ b/app/assets/javascripts/issue_show/components/app.vue @@ -20,7 +20,6 @@ export default { components: { GlIcon, GlIntersectionObserver, - descriptionComponent, titleComponent, editedComponent, formComponent, @@ -152,6 +151,18 @@ export default { required: false, default: 0, }, + descriptionComponent: { + type: Object, + required: false, + default: () => { + return descriptionComponent; + }, + }, + showTitleBorder: { + type: Boolean, + required: false, + default: true, + }, }, data() { const store = new Store({ @@ -209,6 +220,11 @@ export default { isOpenStatus() { return this.issuableStatus === IssuableStatus.Open; }, + pinnedLinkClasses() { + return this.showTitleBorder + ? 'gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid gl-mb-6' + : ''; + }, statusIcon() { return this.isOpenStatus ? 'issue-open-m' : 'mobile-issue-close'; }, @@ -447,9 +463,11 @@ export default { <pinned-links :zoom-meeting-url="zoomMeetingUrl" :published-incident-url="publishedIncidentUrl" + :class="pinnedLinkClasses" /> - <description-component + <component + :is="descriptionComponent" v-if="state.descriptionHtml" :can-update="canUpdate" :description-html="state.descriptionHtml" diff --git a/app/assets/javascripts/issue_show/components/incident_tabs.vue b/app/assets/javascripts/issue_show/components/incident_tabs.vue new file mode 100644 index 00000000000..f6e82cfaa74 --- /dev/null +++ b/app/assets/javascripts/issue_show/components/incident_tabs.vue @@ -0,0 +1,26 @@ +<script> +import { GlTab, GlTabs } from '@gitlab/ui'; +import DescriptionComponent from './description.vue'; + +export default { + components: { + GlTab, + GlTabs, + DescriptionComponent, + }, +}; +</script> + +<template> + <div> + <gl-tabs + content-class="gl-reset-line-height gl-mt-3" + class="gl-mt-n3" + data-testid="incident-tabs" + > + <gl-tab :title="__('Summary')"> + <description-component v-bind="$attrs" /> + </gl-tab> + </gl-tabs> + </div> +</template> diff --git a/app/assets/javascripts/issue_show/components/pinned_links.vue b/app/assets/javascripts/issue_show/components/pinned_links.vue index a877aa2ac96..36375ca743b 100644 --- a/app/assets/javascripts/issue_show/components/pinned_links.vue +++ b/app/assets/javascripts/issue_show/components/pinned_links.vue @@ -45,7 +45,7 @@ export default { </script> <template> - <div class="border-bottom gl-mb-6 gl-display-flex gl-justify-content-start"> + <div class="gl-display-flex gl-justify-content-start"> <template v-for="(link, i) in pinnedLinks"> <div v-if="link.url" :key="link.id" :class="{ 'gl-pr-3': needsPaddingClass(i) }"> <gl-button diff --git a/app/assets/javascripts/issue_show/incident.js b/app/assets/javascripts/issue_show/incident.js new file mode 100644 index 00000000000..82b862a2195 --- /dev/null +++ b/app/assets/javascripts/issue_show/incident.js @@ -0,0 +1,21 @@ +import Vue from 'vue'; +import issuableApp from './components/app.vue'; +import incidentTabs from './components/incident_tabs.vue'; + +export default function initIssuableApp(issuableData = {}) { + return new Vue({ + el: document.getElementById('js-issuable-app'), + components: { + issuableApp, + }, + render(createElement) { + return createElement('issuable-app', { + props: { + ...issuableData, + descriptionComponent: incidentTabs, + showTitleBorder: false, + }, + }); + }, + }); +} diff --git a/app/assets/javascripts/issue_show/index.js b/app/assets/javascripts/issue_show/issue.js index e170d338408..f9f61d5aa64 100644 --- a/app/assets/javascripts/issue_show/index.js +++ b/app/assets/javascripts/issue_show/issue.js @@ -1,8 +1,7 @@ import Vue from 'vue'; import issuableApp from './components/app.vue'; -import { parseIssuableData } from './utils/parse_data'; -export default function initIssueableApp() { +export default function initIssuableApp(issuableData) { return new Vue({ el: document.getElementById('js-issuable-app'), components: { @@ -10,7 +9,7 @@ export default function initIssueableApp() { }, render(createElement) { return createElement('issuable-app', { - props: parseIssuableData(), + props: issuableData, }); }, }); diff --git a/app/assets/javascripts/pages/projects/issues/show.js b/app/assets/javascripts/pages/projects/issues/show.js index 5ac6c17e09d..a577d2e1ecd 100644 --- a/app/assets/javascripts/pages/projects/issues/show.js +++ b/app/assets/javascripts/pages/projects/issues/show.js @@ -4,14 +4,23 @@ import ShortcutsIssuable from '~/behaviors/shortcuts/shortcuts_issuable'; import ZenMode from '~/zen_mode'; import '~/notes/index'; import { store } from '~/notes/stores'; -import initIssueableApp from '~/issue_show'; +import initIssueApp from '~/issue_show/issue'; +import initIncidentApp from '~/issue_show/incident'; import initIssuableHeaderWarning from '~/vue_shared/components/issuable/init_issuable_header_warning'; import initSentryErrorStackTraceApp from '~/sentry_error_stack_trace'; import initRelatedMergeRequestsApp from '~/related_merge_requests'; import initVueIssuableSidebarApp from '~/issuable_sidebar/sidebar_bundle'; +import { parseIssuableData } from '~/issue_show/utils/parse_data'; export default function() { - initIssueableApp(); + const { issueType, ...issuableData } = parseIssuableData(); + + if (issueType === 'incident') { + initIncidentApp(issuableData); + } else { + initIssueApp(issuableData); + } + initIssuableHeaderWarning(store); initSentryErrorStackTraceApp(); initRelatedMergeRequestsApp(); diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 77170f7de5e..8aaeb92eb7a 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -1033,3 +1033,9 @@ $mr-widget-min-height: 69px; .diff-file-row.is-active { background-color: $gray-50; } + +.merge-request-container { + .flash-container { + @include gl-mb-4; + } +} diff --git a/app/finders/concerns/merged_at_filter.rb b/app/finders/concerns/merged_at_filter.rb index e92bee3934c..581bcca3c25 100644 --- a/app/finders/concerns/merged_at_filter.rb +++ b/app/finders/concerns/merged_at_filter.rb @@ -3,7 +3,6 @@ module MergedAtFilter private - # rubocop: disable CodeReuse/ActiveRecord def by_merged_at(items) return items unless merged_after || merged_before @@ -11,11 +10,8 @@ module MergedAtFilter mr_metrics_scope = mr_metrics_scope.merged_after(merged_after) if merged_after.present? mr_metrics_scope = mr_metrics_scope.merged_before(merged_before) if merged_before.present? - scope = items.joins(:metrics).merge(mr_metrics_scope) - scope = target_project_id_filter_on_metrics(scope) if Feature.enabled?(:improved_mr_merged_at_queries, default_enabled: true) - scope + items.join_metrics.merge(mr_metrics_scope) end - # rubocop: enable CodeReuse/ActiveRecord def merged_after params[:merged_after] @@ -24,10 +20,4 @@ module MergedAtFilter def merged_before params[:merged_before] end - - # rubocop: disable CodeReuse/ActiveRecord - def target_project_id_filter_on_metrics(scope) - scope.where(MergeRequest.arel_table[:target_project_id].eq(MergeRequest::Metrics.arel_table[:target_project_id])) - end - # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/graphql/resolvers/merge_requests_resolver.rb b/app/graphql/resolvers/merge_requests_resolver.rb index e428e9f115f..677f84e5795 100644 --- a/app/graphql/resolvers/merge_requests_resolver.rb +++ b/app/graphql/resolvers/merge_requests_resolver.rb @@ -37,6 +37,10 @@ module Resolvers argument :milestone_title, GraphQL::STRING_TYPE, required: false, description: 'Title of the milestone' + argument :sort, Types::MergeRequestSortEnum, + description: 'Sort merge requests by this criteria', + required: false, + default_value: 'created_desc' def self.single ::Resolvers::MergeRequestResolver diff --git a/app/graphql/types/merge_request_sort_enum.rb b/app/graphql/types/merge_request_sort_enum.rb new file mode 100644 index 00000000000..c64ae367a76 --- /dev/null +++ b/app/graphql/types/merge_request_sort_enum.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Types + class MergeRequestSortEnum < IssuableSortEnum + graphql_name 'MergeRequestSort' + description 'Values for sorting merge requests' + + value 'MERGED_AT_ASC', 'Merge time by ascending order', value: :merged_at_asc + value 'MERGED_AT_DESC', 'Merge time by descending order', value: :merged_at_desc + end +end diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index d68ae1a56ca..7b73af4bd09 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -327,7 +327,8 @@ module ApplicationSettingsHelper :group_import_limit, :group_export_limit, :group_download_export_limit, - :wiki_page_max_content_bytes + :wiki_page_max_content_bytes, + :container_registry_delete_tags_service_timeout ] end diff --git a/app/helpers/container_registry_helper.rb b/app/helpers/container_registry_helper.rb new file mode 100644 index 00000000000..9a5d84a90dd --- /dev/null +++ b/app/helpers/container_registry_helper.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module ContainerRegistryHelper + def limit_delete_tags_service? + Feature.enabled?(:container_registry_expiration_policies_throttling) && + ContainerRegistry::Client.supports_tag_delete? + end +end diff --git a/app/helpers/issuables_helper.rb b/app/helpers/issuables_helper.rb index 0b859a39c4f..398e76b6697 100644 --- a/app/helpers/issuables_helper.rb +++ b/app/helpers/issuables_helper.rb @@ -292,6 +292,7 @@ module IssuablesHelper { hasClosingMergeRequest: issuable.merge_requests_count(current_user) != 0, + issueType: issuable.issue_type, zoomMeetingUrl: ZoomMeeting.canonical_meeting_url(issuable), sentryIssueIdentifier: SentryIssue.find_by(issue: issuable)&.sentry_issue_identifier # rubocop:disable CodeReuse/ActiveRecord } @@ -301,8 +302,8 @@ module IssuablesHelper return { groupPath: parent.path } if parent.is_a?(Group) { - projectPath: ref_project.path, - projectNamespace: ref_project.namespace.full_path + projectPath: ref_project.path, + projectNamespace: ref_project.namespace.full_path } end diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 6666a04e71d..83576edb866 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -282,6 +282,9 @@ class ApplicationSetting < ApplicationRecord validates :hashed_storage_enabled, inclusion: { in: [true], message: _("Hashed storage can't be disabled anymore for new projects") } + validates :container_registry_delete_tags_service_timeout, + numericality: { only_integer: true, greater_than_or_equal_to: 0 } + SUPPORTED_KEY_TYPES.each do |type| validates :"#{type}_key_restriction", presence: true, key_restriction: { type: type } end diff --git a/app/models/application_setting_implementation.rb b/app/models/application_setting_implementation.rb index 5a5fc02c112..82304671e4e 100644 --- a/app/models/application_setting_implementation.rb +++ b/app/models/application_setting_implementation.rb @@ -163,7 +163,8 @@ module ApplicationSettingImplementation user_default_external: false, user_default_internal_regex: nil, user_show_add_ssh_key_message: true, - wiki_page_max_content_bytes: 50.megabytes + wiki_page_max_content_bytes: 50.megabytes, + container_registry_delete_tags_service_timeout: 100 } end diff --git a/app/models/atlassian/identity.rb b/app/models/atlassian/identity.rb new file mode 100644 index 00000000000..906f2be0fbf --- /dev/null +++ b/app/models/atlassian/identity.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Atlassian + class Identity < ApplicationRecord + self.table_name = 'atlassian_identities' + + belongs_to :user + + validates :extern_uid, presence: true, uniqueness: true + validates :user, presence: true, uniqueness: true + + attr_encrypted :token, + mode: :per_attribute_iv, + key: Settings.attr_encrypted_db_key_base_truncated, + algorithm: 'aes-256-gcm', + encode: false, + encode_iv: false + + attr_encrypted :refresh_token, + mode: :per_attribute_iv, + key: Settings.attr_encrypted_db_key_base_truncated, + algorithm: 'aes-256-gcm', + encode: false, + encode_iv: false + end +end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index fd73b0d1e04..618fa06745e 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -251,6 +251,15 @@ class MergeRequest < ApplicationRecord joins(:notes).where(notes: { commit_id: sha }) end scope :join_project, -> { joins(:target_project) } + scope :join_metrics, -> do + query = joins(:metrics) + + if Feature.enabled?(:improved_mr_merged_at_queries, default_enabled: true) + query = query.where(MergeRequest.arel_table[:target_project_id].eq(MergeRequest::Metrics.arel_table[:target_project_id])) + end + + query + end scope :references_project, -> { references(:target_project) } scope :with_api_entity_associations, -> { preload_routables @@ -264,6 +273,14 @@ class MergeRequest < ApplicationRecord where("target_branch LIKE ?", ApplicationRecord.sanitize_sql_like(wildcard_branch_name).tr('*', '%')) end scope :by_target_branch, ->(branch_name) { where(target_branch: branch_name) } + scope :order_merged_at, ->(direction) do + query = join_metrics.order(Gitlab::Database.nulls_last_order('merge_request_metrics.merged_at', direction)) + + # Add `merge_request_metrics.merged_at` to the `SELECT` in order to make the keyset pagination work. + query.select(*query.arel.projections, MergeRequest::Metrics.arel_table[:merged_at].as('"merge_request_metrics.merged_at"')) + end + scope :order_merged_at_asc, -> { order_merged_at('ASC') } + scope :order_merged_at_desc, -> { order_merged_at('DESC') } scope :preload_source_project, -> { preload(:source_project) } scope :preload_target_project, -> { preload(:target_project) } scope :preload_routables, -> do @@ -320,6 +337,15 @@ class MergeRequest < ApplicationRecord .pluck(:target_branch) end + def self.sort_by_attribute(method, excluded_labels: []) + case method.to_s + when 'merged_at', 'merged_at_asc' then order_merged_at_asc.with_order_id_desc + when 'merged_at_desc' then order_merged_at_desc.with_order_id_desc + else + super + end + end + def rebase_in_progress? rebase_jid.present? && Gitlab::SidekiqStatus.running?(rebase_jid) end diff --git a/app/models/service.rb b/app/models/service.rb index 148c554119f..262806cd0f0 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -351,10 +351,10 @@ class Service < ApplicationRecord { success: result.present?, result: result } end - # Disable test for instance-level services. + # Disable test for instance-level and group-level services. # https://gitlab.com/gitlab-org/gitlab/-/issues/213138 def can_test? - !instance? + !instance? && !group_id end # Returns a hash of the properties that have been assigned a new value since last save, diff --git a/app/models/snippet.rb b/app/models/snippet.rb index eb3960ff12b..0179176ba9e 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -345,6 +345,10 @@ class Snippet < ApplicationRecord repository.ls_files(ref) end + def multiple_files? + list_files(repository.root_ref).size > 1 + end + class << self # Searches for snippets with a matching title, description or file name. # diff --git a/app/models/user.rb b/app/models/user.rb index 300e918513a..355a174ba9a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -181,6 +181,7 @@ class User < ApplicationRecord has_one :user_detail has_one :user_highest_role has_one :user_canonical_email + has_one :atlassian_identity, class_name: 'Atlassian::Identity' has_many :reviews, foreign_key: :author_id, inverse_of: :author diff --git a/app/services/ci/parse_dotenv_artifact_service.rb b/app/services/ci/parse_dotenv_artifact_service.rb index fcbdc94c097..71b306864b2 100644 --- a/app/services/ci/parse_dotenv_artifact_service.rb +++ b/app/services/ci/parse_dotenv_artifact_service.rb @@ -54,7 +54,7 @@ module Ci end def scan_line!(line) - result = line.scan(/^(.*)=(.*)$/).last + result = line.scan(/^(.*?)=(.*)$/).last raise ParserError, 'Invalid Format' if result.nil? diff --git a/app/services/projects/container_repository/gitlab/delete_tags_service.rb b/app/services/projects/container_repository/gitlab/delete_tags_service.rb index 18049648e26..cee94b994a3 100644 --- a/app/services/projects/container_repository/gitlab/delete_tags_service.rb +++ b/app/services/projects/container_repository/gitlab/delete_tags_service.rb @@ -5,6 +5,11 @@ module Projects module Gitlab class DeleteTagsService include BaseServiceUtility + include ::Gitlab::Utils::StrongMemoize + + DISABLED_TIMEOUTS = [nil, 0].freeze + + TimeoutError = Class.new(StandardError) def initialize(container_repository, tag_names) @container_repository = container_repository @@ -17,12 +22,42 @@ module Projects def execute return success(deleted: []) if @tag_names.empty? + delete_tags + rescue TimeoutError => e + ::Gitlab::ErrorTracking.track_exception(e, tags_count: @tag_names&.size, container_repository_id: @container_repository&.id) + error('timeout while deleting tags') + end + + private + + def delete_tags + start_time = Time.zone.now + deleted_tags = @tag_names.select do |name| + raise TimeoutError if timeout?(start_time) + @container_repository.delete_tag_by_name(name) end deleted_tags.any? ? success(deleted: deleted_tags) : error('could not delete tags') end + + def timeout?(start_time) + return false unless throttling_enabled? + return false if service_timeout.in?(DISABLED_TIMEOUTS) + + (Time.zone.now - start_time) > service_timeout + end + + def throttling_enabled? + strong_memoize(:feature_flag) do + Feature.enabled?(:container_registry_expiration_policies_throttling) + end + end + + def service_timeout + ::Gitlab::CurrentSettings.current_application_settings.container_registry_delete_tags_service_timeout + end end end end diff --git a/app/services/projects/container_repository/third_party/delete_tags_service.rb b/app/services/projects/container_repository/third_party/delete_tags_service.rb index 6504172109e..404642acf72 100644 --- a/app/services/projects/container_repository/third_party/delete_tags_service.rb +++ b/app/services/projects/container_repository/third_party/delete_tags_service.rb @@ -15,7 +15,7 @@ module Projects # This is a hack as the registry doesn't support deleting individual # tags. This code effectively pushes a dummy image and assigns the tag to it. # This way when the tag is deleted only the dummy image is affected. - # This is used to preverse compatibility with third-party registries that + # This is used to preserve compatibility with third-party registries that # don't support fast delete. # See https://gitlab.com/gitlab-org/gitlab/issues/15737 for a discussion def execute diff --git a/app/views/admin/application_settings/_registry.html.haml b/app/views/admin/application_settings/_registry.html.haml index fea3ff4c3ba..8a2de6f53b7 100644 --- a/app/views/admin/application_settings/_registry.html.haml +++ b/app/views/admin/application_settings/_registry.html.haml @@ -14,5 +14,11 @@ .form-text.text-muted = _("Existing projects will be able to use expiration policies. Avoid enabling this if an external Container Registry is being used, as there is a performance risk if many images exist on one project.") = link_to icon('question-circle'), help_page_path('user/packages/container_registry/index', anchor: 'use-with-external-container-registries') + - if limit_delete_tags_service? + .form-group + = f.label :container_registry_delete_tags_service_timeout, _('Cleanup policy maximum processing time (seconds)'), class: 'label-bold' + = f.number_field :container_registry_delete_tags_service_timeout, min: 0, class: 'form-control' + .form-text.text-muted + = _("Tags are deleted until the timeout is reached. Any remaining tags are included the next time the policy runs. To remove the time limit, set it to 0.") = f.submit 'Save changes', class: "btn btn-success" diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index 746d613934c..735a7fa4fea 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -1,5 +1,5 @@ - @gfm_form = true -- @content_class = "limit-container-width" unless fluid_layout +- @content_class = "merge-request-container#{' limit-container-width' unless fluid_layout}" - add_to_breadcrumbs _("Merge Requests"), project_merge_requests_path(@project) - breadcrumb_title @merge_request.to_reference - page_title "#{@merge_request.title} (#{@merge_request.to_reference})", _("Merge Requests") diff --git a/app/views/shared/wikis/_form.html.haml b/app/views/shared/wikis/_form.html.haml index 4d64521f9b0..1fd2194e25b 100644 --- a/app/views/shared/wikis/_form.html.haml +++ b/app/views/shared/wikis/_form.html.haml @@ -21,7 +21,7 @@ .col-sm-12 = f.text_field :title, class: 'form-control qa-wiki-title-textbox', value: @page.title, required: true, autofocus: !@page.persisted?, placeholder: s_('Wiki|Page title') %span.d-inline-block.mw-100.gl-mt-2 - = icon('lightbulb-o') + = sprite_icon('bulb', size: 12, css_class: 'gl-mr-n1') - if @page.persisted? = s_("WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title.") = link_to icon('question-circle'), help_page_path('user/project/wiki/index', anchor: 'moving-a-wiki-page'), diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index b85c3d862cd..9f8129c8c08 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -17,13 +17,13 @@ = sprite_icon('pencil') - elsif current_user - if @user.abuse_report - %button{ class: link_classes + 'btn btn-danger mr-1', title: s_('UserProfile|Already reported for abuse'), - data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } } - = icon('exclamation-circle') + %button{ class: link_classes + 'btn btn-danger', title: s_('UserProfile|Already reported for abuse'), + data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } }> + = sprite_icon('error') - else = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: link_classes + 'btn', title: s_('UserProfile|Report abuse'), data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do - = icon('exclamation-circle') + = sprite_icon('error') - if can?(current_user, :read_user_profile, @user) = link_to user_path(@user, rss_url_options), class: link_classes + 'btn btn-svg btn-default has-tooltip', title: s_('UserProfile|Subscribe'), 'aria-label': 'Subscribe' do = sprite_icon('rss', css_class: 'qa-rss-icon') |