diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-13 15:09:03 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-11-13 15:09:03 +0300 |
commit | 7f59234892f27812dc91044cd63a6a4655e26263 (patch) | |
tree | c9261d822970270d7c5711e2028d5422fb1fdcd4 | |
parent | c21064ccfd3c8d40e82ee92cc709bf0c8bf5bac7 (diff) |
Add latest changes from gitlab-org/gitlab@master
44 files changed, 241 insertions, 359 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 3827fff079c..7f469221da2 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -164,7 +164,7 @@ - "vendor/assets/**/*" - ".gitlab/ci/**/*" - ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}" - - ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml" + - ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,rubocop_manual_todo,scss-lint}.yml" - "*_VERSION" - "Gemfile{,.lock}" - "Rakefile" @@ -186,7 +186,7 @@ - "vendor/assets/**/*" - ".gitlab/ci/**/*" - ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}" - - ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml" + - ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,rubocop_manual_todo,scss-lint}.yml" - "*_VERSION" - "Gemfile{,.lock}" - "Rakefile" @@ -210,7 +210,7 @@ - "vendor/assets/**/*" - ".gitlab/ci/**/*" - ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}" - - ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml" + - ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,rubocop_manual_todo,scss-lint}.yml" - "*_VERSION" - "Gemfile{,.lock}" - "Rakefile" @@ -231,7 +231,7 @@ - "vendor/assets/**/*" - ".gitlab/ci/**/*" - ".{eslintignore,gitattributes,nvmrc,prettierrc,stylelintrc,yamllint}" - - ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,scss-lint}.yml" + - ".{codeclimate,eslintrc,gitlab-ci,haml-lint,haml-lint_todo,rubocop,rubocop_todo,rubocop_manual_todo,scss-lint}.yml" - "*_VERSION" - "Gemfile{,.lock}" - "Rakefile" diff --git a/.rubocop_manual_todo.yml b/.rubocop_manual_todo.yml index 3fe46000e5e..330c7906895 100644 --- a/.rubocop_manual_todo.yml +++ b/.rubocop_manual_todo.yml @@ -43,8 +43,6 @@ Graphql/IDType: Graphql/ResolverType: Exclude: - - 'app/graphql/resolvers/assigned_merge_requests_resolver.rb' - - 'app/graphql/resolvers/authored_merge_requests_resolver.rb' - 'app/graphql/resolvers/base_resolver.rb' - 'app/graphql/resolvers/ci/pipeline_stages_resolver.rb' - 'app/graphql/resolvers/commit_pipelines_resolver.rb' @@ -61,7 +59,6 @@ Graphql/ResolverType: - 'app/graphql/resolvers/project_pipelines_resolver.rb' - 'app/graphql/resolvers/projects/snippets_resolver.rb' - 'app/graphql/resolvers/snippets_resolver.rb' - - 'app/graphql/resolvers/user_merge_requests_resolver.rb' - 'app/graphql/resolvers/users/group_count_resolver.rb' - 'app/graphql/resolvers/users/snippets_resolver.rb' - 'ee/app/graphql/resolvers/ci/jobs_resolver.rb' diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION index d9b7ec5a2f7..a99caf2cdef 100644 --- a/GITLAB_WORKHORSE_VERSION +++ b/GITLAB_WORKHORSE_VERSION @@ -1 +1 @@ -8.53.0 +8.54.0 diff --git a/app/assets/javascripts/issuable_list/components/issuable_item.vue b/app/assets/javascripts/issuable_list/components/issuable_item.vue index 05bc3185cc8..1ee794ab208 100644 --- a/app/assets/javascripts/issuable_list/components/issuable_item.vue +++ b/app/assets/javascripts/issuable_list/components/issuable_item.vue @@ -84,6 +84,14 @@ export default { } return {}; }, + showDiscussions() { + return typeof this.issuable.userDiscussionsCount === 'number'; + }, + showIssuableMeta() { + return Boolean( + this.hasSlotContents('status') || this.showDiscussions || this.issuable.assignees, + ); + }, }, methods: { hasSlotContents(slotName) { @@ -166,6 +174,7 @@ export default { <span class="author">{{ author.name }}</span> </gl-link> </span> + <slot name="timeframe"></slot> <gl-label v-for="(label, index) in labels" @@ -181,10 +190,26 @@ export default { </div> </div> <div class="issuable-meta"> - <ul v-if="hasSlotContents('status') || issuable.assignees" class="controls"> + <ul v-if="showIssuableMeta" class="controls"> <li v-if="hasSlotContents('status')" class="issuable-status"> <slot name="status"></slot> </li> + <li + v-if="showDiscussions" + data-testid="issuable-discussions" + class="issuable-comments gl-display-none gl-display-sm-block" + > + <gl-link + v-gl-tooltip:tooltipcontainer.top + :title="__('Comments')" + :href="`${issuable.webUrl}#notes`" + :class="{ 'no-comments': !issuable.userDiscussionsCount }" + class="gl-reset-color!" + > + <gl-icon name="comments" /> + {{ issuable.userDiscussionsCount }} + </gl-link> + </li> <li v-if="assignees.length" class="gl-display-flex"> <issuable-assignees :assignees="issuable.assignees" diff --git a/app/assets/javascripts/issuable_list/components/issuable_list_root.vue b/app/assets/javascripts/issuable_list/components/issuable_list_root.vue index 0ac51c81d6f..b2312c55f01 100644 --- a/app/assets/javascripts/issuable_list/components/issuable_list_root.vue +++ b/app/assets/javascripts/issuable_list/components/issuable_list_root.vue @@ -269,6 +269,9 @@ export default { <template #author> <slot name="author" :author="issuable.author"></slot> </template> + <template #timeframe> + <slot name="timeframe" :issuable="issuable"></slot> + </template> <template #status> <slot name="status" :issuable="issuable"></slot> </template> diff --git a/app/assets/javascripts/repository/components/tree_action_link.vue b/app/assets/javascripts/repository/components/tree_action_link.vue index 72764f3ccc9..c5ab150adaf 100644 --- a/app/assets/javascripts/repository/components/tree_action_link.vue +++ b/app/assets/javascripts/repository/components/tree_action_link.vue @@ -24,5 +24,5 @@ export default { </script> <template> - <gl-link :href="path" :class="cssClass" class="btn">{{ text }}</gl-link> + <gl-link :href="path" :class="cssClass" class="btn gl-button">{{ text }}</gl-link> </template> diff --git a/app/assets/javascripts/vue_shared/components/actions_button.vue b/app/assets/javascripts/vue_shared/components/actions_button.vue index 9b21de19185..cb4c5f20377 100644 --- a/app/assets/javascripts/vue_shared/components/actions_button.vue +++ b/app/assets/javascripts/vue_shared/components/actions_button.vue @@ -61,7 +61,6 @@ export default { <gl-dropdown v-if="hasMultipleActions" v-gl-tooltip="selectedAction.tooltip" - class="gl-button-deprecated-adapter" :text="selectedAction.text" :split-href="selectedAction.href" :variant="variant" diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue index 96dce959828..97b4ceda033 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue @@ -75,6 +75,11 @@ export default { type: String, required: true, }, + suggestionsListClass: { + type: String, + required: false, + default: '', + }, }, data() { let selectedSortOption = this.sortOptions[0]?.sortDirection?.descending; @@ -315,6 +320,7 @@ export default { :placeholder="searchInputPlaceholder" :available-tokens="tokens" :history-items="filteredRecentSearches" + :suggestions-list-class="suggestionsListClass" class="flex-grow-1" @history-item-selected="handleHistoryItemSelected" @clear-history="handleClearHistory" diff --git a/app/assets/javascripts/vue_shared/components/web_ide_link.vue b/app/assets/javascripts/vue_shared/components/web_ide_link.vue index 877414519f7..dbb1a075e76 100644 --- a/app/assets/javascripts/vue_shared/components/web_ide_link.vue +++ b/app/assets/javascripts/vue_shared/components/web_ide_link.vue @@ -169,7 +169,7 @@ export default { </script> <template> - <div class="d-inline-block gl-ml-3"> + <div class="gl-sm-ml-3"> <actions-button :actions="actions" :selected-key="selection" diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 73fe76f139f..429181c2ad4 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -62,7 +62,7 @@ .tree-controls { margin-bottom: 10px; - .btn, + .btn:not(.dropdown-toggle-split), .dropdown, .btn-group { width: 100%; diff --git a/app/graphql/resolvers/assigned_merge_requests_resolver.rb b/app/graphql/resolvers/assigned_merge_requests_resolver.rb index 172a8e298ad..30415ef5d2d 100644 --- a/app/graphql/resolvers/assigned_merge_requests_resolver.rb +++ b/app/graphql/resolvers/assigned_merge_requests_resolver.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Resolvers - class AssignedMergeRequestsResolver < UserMergeRequestsResolver + class AssignedMergeRequestsResolver < UserMergeRequestsResolverBase + type ::Types::MergeRequestType.connection_type, null: true accept_author def user_role diff --git a/app/graphql/resolvers/authored_merge_requests_resolver.rb b/app/graphql/resolvers/authored_merge_requests_resolver.rb index bc796f8685a..1426ca83c06 100644 --- a/app/graphql/resolvers/authored_merge_requests_resolver.rb +++ b/app/graphql/resolvers/authored_merge_requests_resolver.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true module Resolvers - class AuthoredMergeRequestsResolver < UserMergeRequestsResolver + class AuthoredMergeRequestsResolver < UserMergeRequestsResolverBase + type ::Types::MergeRequestType.connection_type, null: true accept_assignee def user_role diff --git a/app/graphql/resolvers/user_merge_requests_resolver.rb b/app/graphql/resolvers/user_merge_requests_resolver_base.rb index af3ad48e8aa..47967fe69f9 100644 --- a/app/graphql/resolvers/user_merge_requests_resolver.rb +++ b/app/graphql/resolvers/user_merge_requests_resolver_base.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true module Resolvers - class UserMergeRequestsResolver < MergeRequestsResolver + class UserMergeRequestsResolverBase < MergeRequestsResolver include ResolvesProject argument :project_path, GraphQL::STRING_TYPE, diff --git a/app/graphql/types/user_type.rb b/app/graphql/types/user_type.rb index 735bf06337b..11c5369f726 100644 --- a/app/graphql/types/user_type.rb +++ b/app/graphql/types/user_type.rb @@ -46,10 +46,10 @@ module Types resolver: Resolvers::UserStarredProjectsResolver # Merge request field: MRs can be either authored or assigned: - field :authored_merge_requests, Types::MergeRequestType.connection_type, null: true, + field :authored_merge_requests, resolver: Resolvers::AuthoredMergeRequestsResolver, description: 'Merge Requests authored by the user' - field :assigned_merge_requests, Types::MergeRequestType.connection_type, null: true, + field :assigned_merge_requests, resolver: Resolvers::AssignedMergeRequestsResolver, description: 'Merge Requests assigned to the user' diff --git a/app/models/ci/build_trace_chunk.rb b/app/models/ci/build_trace_chunk.rb index 4b36b7c2a75..1fb8e74269d 100644 --- a/app/models/ci/build_trace_chunk.rb +++ b/app/models/ci/build_trace_chunk.rb @@ -54,9 +54,6 @@ module Ci raise "Unknown store type: #{store}" unless STORE_TYPES.key?(store) - # Can't memoize this because the feature flag may alter this - return fog_store_class.new if store == :fog - STORE_TYPES[store].new end @@ -86,14 +83,6 @@ module Ci def metadata_attributes attribute_names - %w[raw_data] end - - def fog_store_class - if Feature.enabled?(:ci_trace_new_fog_store, default_enabled: true) - Ci::BuildTraceChunks::Fog - else - Ci::BuildTraceChunks::LegacyFog - end - end end def data diff --git a/app/models/ci/build_trace_chunks/legacy_fog.rb b/app/models/ci/build_trace_chunks/legacy_fog.rb deleted file mode 100644 index b710ed2890b..00000000000 --- a/app/models/ci/build_trace_chunks/legacy_fog.rb +++ /dev/null @@ -1,77 +0,0 @@ -# frozen_string_literal: true - -module Ci - module BuildTraceChunks - class LegacyFog - def available? - object_store.enabled - end - - def data(model) - connection.get_object(bucket_name, key(model))[:body] - rescue Excon::Error::NotFound - # If the object does not exist in the object storage, this method returns nil. - end - - def set_data(model, new_data) - connection.put_object(bucket_name, key(model), new_data) - end - - def append_data(model, new_data, offset) - if offset > 0 - truncated_data = data(model).to_s.byteslice(0, offset) - new_data = truncated_data + new_data - end - - set_data(model, new_data) - new_data.bytesize - end - - def size(model) - data(model).to_s.bytesize - end - - def delete_data(model) - delete_keys([[model.build_id, model.chunk_index]]) - end - - def keys(relation) - return [] unless available? - - relation.pluck(:build_id, :chunk_index) - end - - def delete_keys(keys) - keys.each do |key| - connection.delete_object(bucket_name, key_raw(*key)) - end - end - - private - - def key(model) - key_raw(model.build_id, model.chunk_index) - end - - def key_raw(build_id, chunk_index) - "tmp/builds/#{build_id.to_i}/chunks/#{chunk_index.to_i}.log" - end - - def bucket_name - return unless available? - - object_store.remote_directory - end - - def connection - return unless available? - - @connection ||= ::Fog::Storage.new(object_store.connection.to_hash.deep_symbolize_keys) - end - - def object_store - Gitlab.config.artifacts.object_store - end - end - end -end diff --git a/app/views/devise/registrations/new.html.haml b/app/views/devise/registrations/new.html.haml index a1e6e701c79..6782e1576b5 100644 --- a/app/views/devise/registrations/new.html.haml +++ b/app/views/devise/registrations/new.html.haml @@ -2,5 +2,5 @@ - add_page_specific_style 'page_bundles/signup' .signup-page - = render 'devise/shared/signup_box' + = render 'devise/shared/signup_box', url: registration_path(resource_name), button_text: _('Register'), show_omniauth_providers: omniauth_enabled? && button_based_providers_enabled? = render 'devise/shared/sign_in_link' diff --git a/app/views/devise/shared/_signup_box.html.haml b/app/views/devise/shared/_signup_box.html.haml index b5c2adb0b9a..0dc98001881 100644 --- a/app/views/devise/shared/_signup_box.html.haml +++ b/app/views/devise/shared/_signup_box.html.haml @@ -2,37 +2,36 @@ - max_username_length = 255 - min_username_length = 2 .gl-mb-3.gl-p-4.gl-border-gray-100.gl-border-1.gl-border-solid.gl-rounded-base - = form_for(resource, as: "new_#{resource_name}", url: registration_path(resource_name), html: { class: "new_new_user gl-show-field-errors", "aria-live" => "assertive" }) do |f| + = form_for(resource, as: "new_#{resource_name}", url: url, html: { class: 'new_user gl-show-field-errors', 'aria-live' => 'assertive' }) do |f| .devise-errors - = render "devise/shared/error_messages", resource: resource + = render 'devise/shared/error_messages', resource: resource - if Feature.enabled?(:invisible_captcha) = invisible_captcha .name.form-row .col.form-group = f.label :first_name, _('First name'), for: 'new_user_first_name', class: 'label-bold' - = f.text_field :first_name, class: 'form-control top js-block-emoji js-validate-length', :data => { :max_length => max_first_name_length, :max_length_message => _("First name is too long (maximum is %{max_length} characters).") % { max_length: max_first_name_length }, :qa_selector => 'new_user_first_name_field' }, required: true, title: _("This field is required.") + = f.text_field :first_name, class: 'form-control top js-block-emoji js-validate-length', :data => { :max_length => max_first_name_length, :max_length_message => s_('SignUp|First name is too long (maximum is %{max_length} characters).') % { max_length: max_first_name_length }, :qa_selector => 'new_user_first_name_field' }, required: true, title: _('This field is required.') .col.form-group = f.label :last_name, _('Last name'), for: 'new_user_last_name', class: 'label-bold' - = f.text_field :last_name, class: "form-control top js-block-emoji js-validate-length", :data => { :max_length => max_last_name_length, :max_length_message => _("Last name is too long (maximum is %{max_length} characters).") % { max_length: max_last_name_length }, :qa_selector => 'new_user_last_name_field' }, required: true, title: _("This field is required.") + = f.text_field :last_name, class: 'form-control top js-block-emoji js-validate-length', :data => { :max_length => max_last_name_length, :max_length_message => s_('SignUp|Last name is too long (maximum is %{max_length} characters).') % { max_length: max_last_name_length }, :qa_selector => 'new_user_last_name_field' }, required: true, title: _('This field is required.') .username.form-group = f.label :username, class: 'label-bold' - = f.text_field :username, class: "form-control middle js-block-emoji js-validate-length js-validate-username", :data => { :min_length => min_username_length, :min_length_message => s_("SignUp|Username is too short (minimum is %{min_length} characters).") % { min_length: min_username_length }, :max_length => max_username_length, :max_length_message => s_("SignUp|Username is too long (maximum is %{max_length} characters).") % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _("Please create a username with only alphanumeric characters.") + = f.text_field :username, class: 'form-control middle js-block-emoji js-validate-length js-validate-username', :data => { :api_path => suggestion_path, :min_length => min_username_length, :min_length_message => s_('SignUp|Username is too short (minimum is %{min_length} characters).') % { min_length: min_username_length }, :max_length => max_username_length, :max_length_message => s_('SignUp|Username is too long (maximum is %{max_length} characters).') % { max_length: max_username_length }, :qa_selector => 'new_user_username_field' }, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, required: true, title: _('Please create a username with only alphanumeric characters.') %p.validation-error.gl-text-red-500.gl-field-error-ignore.gl-mt-2.field-validation.hide= _('Username is already taken.') %p.validation-success.gl-text-green-600.gl-field-error-ignore.gl-mt-2.field-validation.hide= _('Username is available.') %p.validation-pending.gl-field-error-ignore.gl-mt-2.field-validation.hide= _('Checking username availability...') .form-group = f.label :email, class: 'label-bold' - = f.email_field :email, value: @invite_email, class: "form-control middle", data: { qa_selector: 'new_user_email_field' }, required: true, title: _("Please provide a valid email address.") + = f.email_field :email, value: @invite_email, class: 'form-control middle', data: { qa_selector: 'new_user_email_field' }, required: true, title: _('Please provide a valid email address.') .form-group.append-bottom-20#password-strength = f.label :password, class: 'label-bold' - = f.password_field :password, class: "form-control bottom", data: { qa_selector: 'new_user_password_field' }, required: true, pattern: ".{#{@minimum_password_length},}", title: _("Minimum length is %{minimum_password_length} characters.") % { minimum_password_length: @minimum_password_length } - %p.gl-field-hint.text-secondary= _('Minimum length is %{minimum_password_length} characters') % { minimum_password_length: @minimum_password_length } + = f.password_field :password, class: 'form-control bottom', data: { qa_selector: 'new_user_password_field' }, required: true, pattern: ".{#{@minimum_password_length},}", title: s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length } + %p.gl-field-hint.text-secondary= s_('SignUp|Minimum length is %{minimum_password_length} characters.') % { minimum_password_length: @minimum_password_length } %div - if show_recaptcha_sign_up? = recaptcha_tags .submit-container - = f.submit _("Register"), class: "btn gl-button btn-success", data: { qa_selector: 'new_user_register_button' } + = f.submit button_text, class: 'btn gl-button btn-success', data: { qa_selector: 'new_user_register_button' } = render 'devise/shared/terms_of_service_notice' - - if omniauth_enabled? && button_based_providers_enabled? + - if show_omniauth_providers = render 'devise/shared/signup_omniauth_providers' - diff --git a/app/views/projects/_find_file_link.html.haml b/app/views/projects/_find_file_link.html.haml index 74cdb0f7409..c3b4a61c28a 100644 --- a/app/views/projects/_find_file_link.html.haml +++ b/app/views/projects/_find_file_link.html.haml @@ -1,2 +1,2 @@ -= link_to project_find_file_path(@project, @ref), class: 'btn shortcuts-find-file', rel: 'nofollow' do += link_to project_find_file_path(@project, @ref), class: 'gl-button btn shortcuts-find-file', rel: 'nofollow' do = _('Find file') diff --git a/app/views/projects/buttons/_clone.html.haml b/app/views/projects/buttons/_clone.html.haml index 7ce143a86b3..cf58cff7445 100644 --- a/app/views/projects/buttons/_clone.html.haml +++ b/app/views/projects/buttons/_clone.html.haml @@ -2,7 +2,7 @@ - dropdown_class = local_assigns.fetch(:dropdown_class, '') .git-clone-holder.js-git-clone-holder - %a#clone-dropdown.btn.btn-primary.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } } + %a#clone-dropdown.gl-button.btn.btn-primary.clone-dropdown-btn.qa-clone-dropdown{ href: '#', data: { toggle: 'dropdown' } } %span.gl-mr-2.js-clone-dropdown-label = _('Clone') = sprite_icon("chevron-down", css_class: "icon") diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index c04687bd846..0fcbf2ca1eb 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -3,7 +3,7 @@ - if !project.empty_repo? && can?(current_user, :download_code, project) - archive_prefix = "#{project.path}-#{ref.tr('/', '-')}" .project-action-button.dropdown.inline> - %button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download'), 'data-display' => 'static', data: { qa_selector: 'download_source_code_button' } } + %button.gl-button.btn.has-tooltip{ title: s_('DownloadSource|Download'), 'data-toggle' => 'dropdown', 'aria-label' => s_('DownloadSource|Download'), 'data-display' => 'static', data: { qa_selector: 'download_source_code_button' } } = sprite_icon('download') %span.sr-only= _('Select Archive Format') = sprite_icon("chevron-down") diff --git a/app/views/projects/buttons/_xcode_link.html.haml b/app/views/projects/buttons/_xcode_link.html.haml index a8b32fb0ef5..e0f47f1ca3d 100644 --- a/app/views/projects/buttons/_xcode_link.html.haml +++ b/app/views/projects/buttons/_xcode_link.html.haml @@ -1,2 +1,2 @@ -%a.btn.btn-default{ href: xcode_uri_to_repo(@project) } +%a.gl-button.btn.btn-default{ href: xcode_uri_to_repo(@project) } = _("Open in Xcode") diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index dc9fb9e7792..cd6e85d60ed 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -13,7 +13,7 @@ = render 'shared/web_ide_button', blob: nil - if show_xcode_link?(@project) - .project-action-button.project-xcode.inline< + .project-action-button.project-xcode< = render "projects/buttons/xcode_link" = render 'projects/buttons/download', project: @project, ref: @ref diff --git a/changelogs/unreleased/280811-mr-analytics-chart-tooltip-overlays-filter-dropdown.yml b/changelogs/unreleased/280811-mr-analytics-chart-tooltip-overlays-filter-dropdown.yml new file mode 100644 index 00000000000..87236ab3e2b --- /dev/null +++ b/changelogs/unreleased/280811-mr-analytics-chart-tooltip-overlays-filter-dropdown.yml @@ -0,0 +1,5 @@ +--- +title: 'MR Analytics: Fix chart tooltip covering filter dropdown' +merge_request: 47274 +author: +type: changed diff --git a/changelogs/unreleased/alipniagov-update-workhorse-to-8-54.yml b/changelogs/unreleased/alipniagov-update-workhorse-to-8-54.yml new file mode 100644 index 00000000000..c57e1e5ef7d --- /dev/null +++ b/changelogs/unreleased/alipniagov-update-workhorse-to-8-54.yml @@ -0,0 +1,5 @@ +--- +title: Update Workhorse version to 8.54.0 +merge_request: 47625 +author: +type: other diff --git a/changelogs/unreleased/ps-fix-repo-tree-header-button-styles.yml b/changelogs/unreleased/ps-fix-repo-tree-header-button-styles.yml new file mode 100644 index 00000000000..222884a7960 --- /dev/null +++ b/changelogs/unreleased/ps-fix-repo-tree-header-button-styles.yml @@ -0,0 +1,5 @@ +--- +title: Update button styles in project tree header +merge_request: 47562 +author: +type: other diff --git a/changelogs/unreleased/remove-ci_always_refresh_merge_requests_from_beginning-flag.yml b/changelogs/unreleased/remove-ci_always_refresh_merge_requests_from_beginning-flag.yml new file mode 100644 index 00000000000..54879f5fc4f --- /dev/null +++ b/changelogs/unreleased/remove-ci_always_refresh_merge_requests_from_beginning-flag.yml @@ -0,0 +1,6 @@ +--- +title: Make the Merge Train process flow more resilient by always refreshing merge + requests from beginning +merge_request: 46768 +author: +type: fixed diff --git a/changelogs/unreleased/sh-remove-legacy-fog-ff.yml b/changelogs/unreleased/sh-remove-legacy-fog-ff.yml new file mode 100644 index 00000000000..9d8aaa321b2 --- /dev/null +++ b/changelogs/unreleased/sh-remove-legacy-fog-ff.yml @@ -0,0 +1,5 @@ +--- +title: Remove ci_trace_new_fog_store feature flag +merge_request: 47522 +author: +type: changed diff --git a/config/feature_flags/development/ci_always_refresh_merge_requests_from_beginning.yml b/config/feature_flags/development/ci_always_refresh_merge_requests_from_beginning.yml deleted file mode 100644 index 387c921cb5b..00000000000 --- a/config/feature_flags/development/ci_always_refresh_merge_requests_from_beginning.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: ci_always_refresh_merge_requests_from_beginning -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45232 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/268215 -milestone: '13.5' -type: development -group: group::continuous integration -default_enabled: false diff --git a/config/feature_flags/development/ci_trace_new_fog_store.yml b/config/feature_flags/development/ci_trace_new_fog_store.yml deleted file mode 100644 index b7da65be57b..00000000000 --- a/config/feature_flags/development/ci_trace_new_fog_store.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: ci_trace_new_fog_store -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46209 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273405 -milestone: '13.6' -type: development -group: group::testing -default_enabled: true diff --git a/doc/api/boards.md b/doc/api/boards.md index fca22596c58..228c0ca322b 100644 --- a/doc/api/boards.md +++ b/doc/api/boards.md @@ -33,6 +33,7 @@ Example response: [ { "id" : 1, + "name": "board1", "project": { "id": 5, "name": "Diaspora Project Site", diff --git a/doc/subscriptions/self_managed/index.md b/doc/subscriptions/self_managed/index.md index 5bb4ae569fb..a63c2830909 100644 --- a/doc/subscriptions/self_managed/index.md +++ b/doc/subscriptions/self_managed/index.md @@ -52,8 +52,23 @@ and blocked, go to **Admin Area > Overview > Dashboard** and select **Users stat in the **Users** section. For more details, see [Users statistics](../../user/admin_area/index.md#users-statistics). -NOTE: **Note:** -If you have LDAP integration enabled, anyone in the configured domain can sign up for a GitLab account. This can result in an unexpected bill at time of renewal. Consider [disabling new signups](../../user/admin_area/settings/sign_up_restrictions.md) and managing new users manually instead. +### Tips for managing users and subscription seats + +Managing the number of users against the number of subscription seats can be a challenge: + +- If LDAP integration is enabled, anyone in the configured domain can sign up for a GitLab account. + This can result in an unexpected bill at time of renewal. +- If sign-up is enabled on your instance, anyone who can access the instance can sign up for an + account. + +GitLab has several features which can help you manage the number of users: + +- Enable the [**Require administrator approval for new sign ups**](../../user/admin_area/settings/sign_up_restrictions.md#require-administrator-approval-for-new-sign-ups) + option. +- Enable the [User cap](../../user/admin_area/settings/sign_up_restrictions.md#user-cap) + option. **Available in GitLab 13.6 and later**. +- [Disable new sign-ups](../../user/admin_area/settings/sign_up_restrictions.md), and instead manage new + users manually. ## Obtain a subscription diff --git a/doc/user/admin_area/settings/sign_up_restrictions.md b/doc/user/admin_area/settings/sign_up_restrictions.md index 39056ed804a..7bf05f00552 100644 --- a/doc/user/admin_area/settings/sign_up_restrictions.md +++ b/doc/user/admin_area/settings/sign_up_restrictions.md @@ -46,6 +46,14 @@ To enforce confirmation of the email address used for new sign ups: 1. Go to **Admin Area > Settings > General** and expand **Sign-up restrictions**. 1. Select the **Enable email restrictions for sign ups** checkbox, then select **Save changes**. +## User cap **(CORE ONLY)** + +> [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/4315) in GitLab 13.6. + +When the number of users reaches the user cap, any user who is added or requests access must be +[approved](../approving_users.md#approving-a-user) by an administrator before they can start using +their account. + ## Soft email confirmation > - [Introduced](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/47003) in GitLab 12.2. diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 6a973a7647f..5537412f1b8 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -543,6 +543,12 @@ msgstr "" msgid "%{issuesSize} with a limit of %{maxIssueCount}" msgstr "" +msgid "%{labelStart}Actual response:%{labelEnd} %{headers}" +msgstr "" + +msgid "%{labelStart}Assert:%{labelEnd} %{assertion}" +msgstr "" + msgid "%{labelStart}Class:%{labelEnd} %{class}" msgstr "" @@ -558,9 +564,6 @@ msgstr "" msgid "%{labelStart}File:%{labelEnd} %{file}" msgstr "" -msgid "%{labelStart}Headers:%{labelEnd} %{headers}" -msgstr "" - msgid "%{labelStart}Image:%{labelEnd} %{image}" msgstr "" @@ -576,13 +579,13 @@ msgstr "" msgid "%{labelStart}Scanner:%{labelEnd} %{scanner}" msgstr "" -msgid "%{labelStart}Severity:%{labelEnd} %{severity}" +msgid "%{labelStart}Sent request:%{labelEnd} %{headers}" msgstr "" -msgid "%{labelStart}Status:%{labelEnd} %{status}" +msgid "%{labelStart}Severity:%{labelEnd} %{severity}" msgstr "" -msgid "%{labelStart}URL:%{labelEnd} %{url}" +msgid "%{labelStart}Unmodified response:%{labelEnd} %{headers}" msgstr "" msgid "%{label_for_message} unavailable" @@ -9457,9 +9460,33 @@ msgstr "" msgid "DevopsAdoption|Add new segment" msgstr "" +msgid "DevopsAdoption|Approvals" +msgstr "" + +msgid "DevopsAdoption|Deploys" +msgstr "" + msgid "DevopsAdoption|DevOps adoption uses segments to track adoption across key features. Segments are a way to track multiple related projects and groups at once. For example, you could create a segment for the engineering department or a particular product team." msgstr "" +msgid "DevopsAdoption|Issues" +msgstr "" + +msgid "DevopsAdoption|MRs" +msgstr "" + +msgid "DevopsAdoption|Pipelines" +msgstr "" + +msgid "DevopsAdoption|Runners" +msgstr "" + +msgid "DevopsAdoption|Scanning" +msgstr "" + +msgid "DevopsAdoption|Segment" +msgstr "" + msgid "DevopsReport|Adoption" msgstr "" @@ -11950,9 +11977,6 @@ msgstr "" msgid "First name" msgstr "" -msgid "First name is too long (maximum is %{max_length} characters)." -msgstr "" - msgid "First seen" msgstr "" @@ -15697,9 +15721,6 @@ msgstr "" msgid "Last name" msgstr "" -msgid "Last name is too long (maximum is %{max_length} characters)." -msgstr "" - msgid "Last reply by" msgstr "" @@ -17572,12 +17593,6 @@ msgstr "" msgid "Minimum interval in days" msgstr "" -msgid "Minimum length is %{minimum_password_length} characters" -msgstr "" - -msgid "Minimum length is %{minimum_password_length} characters." -msgstr "" - msgid "Minimum password length (number of characters)" msgstr "" @@ -22414,9 +22429,6 @@ msgstr "" msgid "Remediations" msgstr "" -msgid "Remember me" -msgstr "" - msgid "Remind later" msgstr "" @@ -24984,10 +24996,13 @@ msgstr "" msgid "Sign-up restrictions" msgstr "" -msgid "SignUp|First Name is too long (maximum is %{max_length} characters)." +msgid "SignUp|First name is too long (maximum is %{max_length} characters)." msgstr "" -msgid "SignUp|Last Name is too long (maximum is %{max_length} characters)." +msgid "SignUp|Last name is too long (maximum is %{max_length} characters)." +msgstr "" + +msgid "SignUp|Minimum length is %{minimum_password_length} characters." msgstr "" msgid "SignUp|Username is too long (maximum is %{max_length} characters)." @@ -30051,6 +30066,12 @@ msgstr "" msgid "Vulnerability|Activity" msgstr "" +msgid "Vulnerability|Actual received response is the one received when this fault was detected" +msgstr "" + +msgid "Vulnerability|Additional Info" +msgstr "" + msgid "Vulnerability|Class" msgstr "" @@ -30099,10 +30120,7 @@ msgstr "" msgid "Vulnerability|Project" msgstr "" -msgid "Vulnerability|Request" -msgstr "" - -msgid "Vulnerability|Response" +msgid "Vulnerability|Request/Response" msgstr "" msgid "Vulnerability|Scanner" @@ -30117,6 +30135,9 @@ msgstr "" msgid "Vulnerability|Status" msgstr "" +msgid "Vulnerability|The unmodified response is the original response that had no mutations done to the request" +msgstr "" + msgid "Wait for the file to load to copy its contents" msgstr "" @@ -31627,6 +31648,9 @@ msgstr "" msgid "ciReport|: Loading resulted in an error" msgstr "" +msgid "ciReport|API Fuzzing" +msgstr "" + msgid "ciReport|All projects" msgstr "" diff --git a/package.json b/package.json index f9a0c721f12..c8ade903791 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "@babel/preset-env": "^7.10.1", "@gitlab/at.js": "1.5.5", "@gitlab/svgs": "1.175.0", - "@gitlab/ui": "23.6.1", + "@gitlab/ui": "23.8.0", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.3-3", "@rails/ujs": "^6.0.3-2", diff --git a/spec/frontend/issuable_list/components/issuable_item_spec.js b/spec/frontend/issuable_list/components/issuable_item_spec.js index 27c3a746090..3a9a0d3fd59 100644 --- a/spec/frontend/issuable_list/components/issuable_item_spec.js +++ b/spec/frontend/issuable_list/components/issuable_item_spec.js @@ -1,5 +1,5 @@ import { shallowMount } from '@vue/test-utils'; -import { GlLink, GlLabel, GlFormCheckbox } from '@gitlab/ui'; +import { GlLink, GlLabel, GlIcon, GlFormCheckbox } from '@gitlab/ui'; import IssuableItem from '~/issuable_list/components/issuable_item.vue'; import IssuableAssignees from '~/vue_shared/components/issue/issue_assignees.vue'; @@ -12,6 +12,7 @@ const createComponent = ({ issuableSymbol = '#', issuable = mockIssuable, slots issuableSymbol, issuable, enableLabelPermalinks: true, + showDiscussions: true, showCheckbox: false, }, slots, @@ -142,6 +143,31 @@ describe('IssuableItem', () => { expect(wrapper.vm.updatedAt).toContain('ago'); }); }); + + describe('showDiscussions', () => { + it.each` + userDiscussionsCount | returnValue + ${0} | ${true} + ${1} | ${true} + ${undefined} | ${false} + ${null} | ${false} + `( + 'returns $returnValue when issuable.userDiscussionsCount is $userDiscussionsCount', + ({ userDiscussionsCount, returnValue }) => { + const wrapperWithDiscussions = createComponent({ + issuableSymbol: '#', + issuable: { + ...mockIssuable, + userDiscussionsCount, + }, + }); + + expect(wrapperWithDiscussions.vm.showDiscussions).toBe(returnValue); + + wrapperWithDiscussions.destroy(); + }, + ); + }); }); describe('methods', () => { @@ -299,6 +325,24 @@ describe('IssuableItem', () => { wrapperWithAuthorSlot.destroy(); }); + it('renders timeframe via slot', () => { + const wrapperWithTimeframeSlot = createComponent({ + issuableSymbol: '#', + issuable: mockIssuable, + slots: { + timeframe: ` + <b class="js-timeframe">Jan 1, 2020 - Mar 31, 2020</b> + `, + }, + }); + const timeframeEl = wrapperWithTimeframeSlot.find('.js-timeframe'); + + expect(timeframeEl.exists()).toBe(true); + expect(timeframeEl.text()).toBe('Jan 1, 2020 - Mar 31, 2020'); + + wrapperWithTimeframeSlot.destroy(); + }); + it('renders gl-label component for each label present within `issuable` prop', () => { const labelsEl = wrapper.findAll(GlLabel); @@ -332,6 +376,18 @@ describe('IssuableItem', () => { wrapperWithStatusSlot.destroy(); }); + it('renders discussions count', () => { + const discussionsEl = wrapper.find('[data-testid="issuable-discussions"]'); + + expect(discussionsEl.exists()).toBe(true); + expect(discussionsEl.find(GlLink).attributes()).toMatchObject({ + title: 'Comments', + href: `${mockIssuable.webUrl}#notes`, + }); + expect(discussionsEl.find(GlIcon).props('name')).toBe('comments'); + expect(discussionsEl.find(GlLink).text()).toContain('2'); + }); + it('renders issuable-assignees component', () => { const assigneesEl = wrapper.find(IssuableAssignees); diff --git a/spec/frontend/issuable_list/mock_data.js b/spec/frontend/issuable_list/mock_data.js index b18e49e2102..e19a337473a 100644 --- a/spec/frontend/issuable_list/mock_data.js +++ b/spec/frontend/issuable_list/mock_data.js @@ -52,6 +52,7 @@ export const mockIssuable = { nodes: mockLabels, }, assignees: [mockAuthor], + userDiscussionsCount: 2, }; export const mockIssuables = [ diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js index 72840ce381f..3fd1d8b7f42 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js @@ -45,6 +45,7 @@ function createComponent(options = {}) { provide: { portalName: 'fake target', alignSuggestions: function fakeAlignSuggestions() {}, + suggestionsListClass: 'custom-class', }, stubs, }); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js index 12b7fd58670..5b7f7d242e9 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js @@ -45,6 +45,7 @@ function createComponent(options = {}) { provide: { portalName: 'fake target', alignSuggestions: function fakeAlignSuggestions() {}, + suggestionsListClass: 'custom-class', }, stubs, }); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js index 3feb05bab35..74172db81c2 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js @@ -50,6 +50,7 @@ function createComponent(options = {}) { provide: { portalName: 'fake target', alignSuggestions: function fakeAlignSuggestions() {}, + suggestionsListClass: 'custom-class', }, stubs, }); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js index 5d91eafbabf..67f9a9c70cc 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js @@ -48,6 +48,7 @@ function createComponent(options = {}) { provide: { portalName: 'fake target', alignSuggestions: function fakeAlignSuggestions() {}, + suggestionsListClass: 'custom-class', }, stubs, }); diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index 23f5a8d929e..dce7b1d30ca 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -135,30 +135,14 @@ RSpec.describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do context 'when data_store is fog' do let(:data_store) { :fog } - context 'when legacy Fog is enabled' do - before do - stub_feature_flags(ci_trace_new_fog_store: false) - build_trace_chunk.send(:unsafe_set_data!, +'Sample data in fog') - end - - it { is_expected.to eq('Sample data in fog') } - - it 'returns a LegacyFog store' do - expect(described_class.get_store_class(data_store)).to be_a(Ci::BuildTraceChunks::LegacyFog) - end + before do + build_trace_chunk.send(:unsafe_set_data!, +'Sample data in fog') end - context 'when new Fog is enabled' do - before do - stub_feature_flags(ci_trace_new_fog_store: true) - build_trace_chunk.send(:unsafe_set_data!, +'Sample data in fog') - end + it { is_expected.to eq('Sample data in fog') } - it { is_expected.to eq('Sample data in fog') } - - it 'returns a new Fog store' do - expect(described_class.get_store_class(data_store)).to be_a(Ci::BuildTraceChunks::Fog) - end + it 'returns a new Fog store' do + expect(described_class.get_store_class(data_store)).to be_a(Ci::BuildTraceChunks::Fog) end end end diff --git a/spec/models/ci/build_trace_chunks/legacy_fog_spec.rb b/spec/models/ci/build_trace_chunks/legacy_fog_spec.rb deleted file mode 100644 index ca4b414b992..00000000000 --- a/spec/models/ci/build_trace_chunks/legacy_fog_spec.rb +++ /dev/null @@ -1,164 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe Ci::BuildTraceChunks::LegacyFog do - let(:data_store) { described_class.new } - - before do - stub_artifacts_object_storage - end - - describe '#available?' do - subject { data_store.available? } - - context 'when object storage is enabled' do - it { is_expected.to be_truthy } - end - - context 'when object storage is disabled' do - before do - stub_artifacts_object_storage(enabled: false) - end - - it { is_expected.to be_falsy } - end - end - - describe '#data' do - subject { data_store.data(model) } - - context 'when data exists' do - let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') } - - it 'returns the data' do - is_expected.to eq('sample data in fog') - end - end - - context 'when data does not exist' do - let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } - - it 'returns nil' do - expect(data_store.data(model)).to be_nil - end - end - end - - describe '#set_data' do - let(:new_data) { 'abc123' } - - context 'when data exists' do - let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') } - - it 'overwrites data' do - expect(data_store.data(model)).to eq('sample data in fog') - - data_store.set_data(model, new_data) - - expect(data_store.data(model)).to eq new_data - end - end - - context 'when data does not exist' do - let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } - - it 'sets new data' do - expect(data_store.data(model)).to be_nil - - data_store.set_data(model, new_data) - - expect(data_store.data(model)).to eq new_data - end - end - end - - describe '#delete_data' do - subject { data_store.delete_data(model) } - - context 'when data exists' do - let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') } - - it 'deletes data' do - expect(data_store.data(model)).to eq('sample data in fog') - - subject - - expect(data_store.data(model)).to be_nil - end - end - - context 'when data does not exist' do - let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } - - it 'does nothing' do - expect(data_store.data(model)).to be_nil - - subject - - expect(data_store.data(model)).to be_nil - end - end - end - - describe '#size' do - context 'when data exists' do - let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'üabcd') } - - it 'returns data bytesize correctly' do - expect(data_store.size(model)).to eq 6 - end - end - - context 'when data does not exist' do - let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } - - it 'returns zero' do - expect(data_store.size(model)).to be_zero - end - end - end - - describe '#keys' do - subject { data_store.keys(relation) } - - let(:build) { create(:ci_build) } - let(:relation) { build.trace_chunks } - - before do - create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 0, build: build) - create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 1, build: build) - end - - it 'returns keys' do - is_expected.to eq([[build.id, 0], [build.id, 1]]) - end - end - - describe '#delete_keys' do - subject { data_store.delete_keys(keys) } - - let(:build) { create(:ci_build) } - let(:relation) { build.trace_chunks } - let(:keys) { data_store.keys(relation) } - - before do - create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 0, build: build) - create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 1, build: build) - end - - it 'deletes multiple data' do - ::Fog::Storage.new(JobArtifactUploader.object_store_credentials).tap do |connection| - expect(connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/0.log")[:body]).to be_present - expect(connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/1.log")[:body]).to be_present - end - - subject - - ::Fog::Storage.new(JobArtifactUploader.object_store_credentials).tap do |connection| - expect { connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/0.log")[:body] }.to raise_error(Excon::Error::NotFound) - expect { connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/1.log")[:body] }.to raise_error(Excon::Error::NotFound) - end - end - end -end diff --git a/yarn.lock b/yarn.lock index d3861c0d69b..a18b1f9beaf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -866,10 +866,10 @@ resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.175.0.tgz#734f341784af1cd1d62d160a17bcdfb61ff7b04d" integrity sha512-gXpc87TGSXIzfAr4QER1Qw1v3P47pBO6BXkma52blgwXVmcFNe3nhQzqsqt66wKNzrIrk3lAcB4GUyPHbPVXpg== -"@gitlab/ui@23.6.1": - version "23.6.1" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-23.6.1.tgz#3b94945166c756a26c9598501ebef0997a3961e7" - integrity sha512-jOEhJUTQmdsV4XEHqY+ray+GHAMa35CsddPFtKjiD8y1YiJFzXES7tVqQr/hXzSAWbBWfxSO1Rvp55+UnCGz5g== +"@gitlab/ui@23.8.0": + version "23.8.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-23.8.0.tgz#fe1807877c191e9e38b476d2cdfc4261facbb36b" + integrity sha512-5CF2jU/d5EX5a1qLHzJujYOTaCze1ZvE9ovK1TbhJ7Va1O0SKB/N53XT8iPOb4MwOzj/zBWDOdsIs+xXxCeOcg== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0" |