diff options
Diffstat (limited to 'app')
19 files changed, 148 insertions, 87 deletions
diff --git a/app/assets/javascripts/alert_management/components/alert_management_list.vue b/app/assets/javascripts/alert_management/components/alert_management_list.vue index b9edbb4b0a0..ce1738f1961 100644 --- a/app/assets/javascripts/alert_management/components/alert_management_list.vue +++ b/app/assets/javascripts/alert_management/components/alert_management_list.vue @@ -13,6 +13,7 @@ import { } from '@gitlab/ui'; import createFlash from '~/flash'; import { s__ } from '~/locale'; +import { joinPaths } from '~/lib/utils/url_utility'; import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue'; import getAlerts from '../graphql/queries/getAlerts.query.graphql'; import { ALERTS_STATUS, ALERTS_STATUS_TABS, ALERTS_SEVERITY_LABELS } from '../constants'; @@ -21,8 +22,11 @@ import updateAlertStatus from '../graphql/mutations/update_alert_status.graphql' import { capitalizeFirstCharacter } from '~/lib/utils/text_utility'; const tdClass = 'table-col d-flex d-md-table-cell align-items-center'; +const bodyTrClass = + 'gl-border-1 gl-border-t-solid gl-border-gray-100 hover-bg-blue-50 hover-gl-cursor-pointer hover-gl-border-b-solid hover-gl-border-blue-200'; export default { + bodyTrClass, i18n: { noAlertsMsg: s__( "AlertManagement|No alerts available to display. If you think you're seeing this message in error, refresh the page.", @@ -62,9 +66,8 @@ export default { { key: 'status', thClass: 'w-15p', - trClass: 'w-15p', label: s__('AlertManagement|Status'), - tdClass: `${tdClass} rounded-bottom text-capitalize`, + tdClass: `${tdClass} rounded-bottom`, }, ], statuses: { @@ -170,6 +173,9 @@ export default { ); }); }, + handleRowClick({ iid }) { + window.location.assign(joinPaths(window.location.pathname, iid, 'details')); + }, }, }; </script> @@ -201,8 +207,9 @@ export default { :fields="$options.fields" :show-empty="true" :busy="loading" - fixed stacked="md" + :tbody-tr-class="$options.bodyTrClass" + @row-clicked="handleRowClick" > <template #cell(severity)="{ item }"> <div diff --git a/app/assets/javascripts/ide/components/commit_sidebar/form.vue b/app/assets/javascripts/ide/components/commit_sidebar/form.vue index f6ca728defc..4cbd33e6ed6 100644 --- a/app/assets/javascripts/ide/components/commit_sidebar/form.vue +++ b/app/assets/javascripts/ide/components/commit_sidebar/form.vue @@ -1,11 +1,13 @@ <script> import { mapState, mapActions, mapGetters } from 'vuex'; import { n__, __ } from '~/locale'; +import { GlModal } from '@gitlab/ui'; import LoadingButton from '~/vue_shared/components/loading_button.vue'; import CommitMessageField from './message_field.vue'; import Actions from './actions.vue'; import SuccessMessage from './success_message.vue'; import { leftSidebarViews, MAX_WINDOW_HEIGHT_COMPACT } from '../../constants'; +import consts from '../../stores/modules/commit/constants'; export default { components: { @@ -13,6 +15,7 @@ export default { LoadingButton, CommitMessageField, SuccessMessage, + GlModal, }, data() { return { @@ -54,7 +57,20 @@ export default { }, methods: { ...mapActions(['updateActivityBarView']), - ...mapActions('commit', ['updateCommitMessage', 'discardDraft', 'commitChanges']), + ...mapActions('commit', [ + 'updateCommitMessage', + 'discardDraft', + 'commitChanges', + 'updateCommitAction', + ]), + commit() { + return this.commitChanges().catch(() => { + this.$refs.createBranchModal.show(); + }); + }, + forceCreateNewBranch() { + return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() => this.commit()); + }, toggleIsCompact() { if (this.currentViewIsCommitView) { this.isCompact = !this.isCompact; @@ -119,13 +135,13 @@ export default { </button> <p class="text-center bold">{{ overviewText }}</p> </div> - <form v-if="!isCompact" ref="formEl" @submit.prevent.stop="commitChanges"> + <form v-if="!isCompact" ref="formEl" @submit.prevent.stop="commit"> <transition name="fade"> <success-message v-show="lastCommitMsg" /> </transition> <commit-message-field :text="commitMessage" :placeholder="preBuiltCommitMessage" @input="updateCommitMessage" - @submit="commitChanges" + @submit="commit" /> <div class="clearfix prepend-top-15"> <actions /> @@ -133,7 +149,7 @@ export default { :loading="submitCommitLoading" :label="commitButtonText" container-class="btn btn-success btn-sm float-left qa-commit-button" - @click="commitChanges" + @click="commit" /> <button v-if="!discardDraftButtonDisabled" @@ -152,6 +168,19 @@ export default { {{ __('Collapse') }} </button> </div> + <gl-modal + ref="createBranchModal" + modal-id="ide-create-branch-modal" + :ok-title="__('Create new branch')" + :title="__('Branch has changed')" + ok-variant="success" + @ok="forceCreateNewBranch" + > + {{ + __(`This branch has changed since you started editing. + Would you like to create a new branch?`) + }} + </gl-modal> </form> </transition> </div> diff --git a/app/assets/javascripts/ide/components/repo_commit_section.vue b/app/assets/javascripts/ide/components/repo_commit_section.vue index f716d884091..530fba49df2 100644 --- a/app/assets/javascripts/ide/components/repo_commit_section.vue +++ b/app/assets/javascripts/ide/components/repo_commit_section.vue @@ -1,15 +1,12 @@ <script> import { mapState, mapActions, mapGetters } from 'vuex'; import tooltip from '~/vue_shared/directives/tooltip'; -import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue'; import CommitFilesList from './commit_sidebar/list.vue'; import EmptyState from './commit_sidebar/empty_state.vue'; -import consts from '../stores/modules/commit/constants'; import { leftSidebarViews, stageKeys } from '../constants'; export default { components: { - DeprecatedModal, CommitFilesList, EmptyState, }, @@ -53,10 +50,6 @@ export default { }, methods: { ...mapActions(['openPendingTab', 'updateViewer', 'updateActivityBarView']), - ...mapActions('commit', ['commitChanges', 'updateCommitAction']), - forceCreateNewBranch() { - return this.updateCommitAction(consts.COMMIT_TO_NEW_BRANCH).then(() => this.commitChanges()); - }, }, stageKeys, }; @@ -64,20 +57,6 @@ export default { <template> <div class="multi-file-commit-panel-section"> - <deprecated-modal - id="ide-create-branch-modal" - :primary-button-label="__('Create new branch')" - :title="__('Branch has changed')" - kind="success" - @submit="forceCreateNewBranch" - > - <template slot="body"> - {{ - __(`This branch has changed since you started editing. - Would you like to create a new branch?`) - }} - </template> - </deprecated-modal> <template v-if="showStageUnstageArea"> <commit-files-list :key-prefix="$options.stageKeys.staged" diff --git a/app/assets/javascripts/ide/stores/modules/commit/actions.js b/app/assets/javascripts/ide/stores/modules/commit/actions.js index 505daa8834d..592c7e15918 100644 --- a/app/assets/javascripts/ide/stores/modules/commit/actions.js +++ b/app/assets/javascripts/ide/stores/modules/commit/actions.js @@ -1,6 +1,6 @@ -import $ from 'jquery'; import { sprintf, __ } from '~/locale'; import flash from '~/flash'; +import httpStatusCodes from '~/lib/utils/http_status'; import * as rootTypes from '../../mutation_types'; import { createCommitPayload, createNewMergeRequestUrl } from '../../utils'; import router from '../../../ide_router'; @@ -215,25 +215,23 @@ export const commitChanges = ({ commit, state, getters, dispatch, rootState, roo ); }) .catch(err => { - if (err.response.status === 400) { - $('#ide-create-branch-modal').modal('show'); - } else { - dispatch( - 'setErrorMessage', - { - text: __('An error occurred while committing your changes.'), - action: () => - dispatch('commitChanges').then(() => - dispatch('setErrorMessage', null, { root: true }), - ), - actionText: __('Please try again'), - }, - { root: true }, - ); - window.dispatchEvent(new Event('resize')); - } - commit(types.UPDATE_LOADING, false); + + // don't catch bad request errors, let the view handle them + if (err.response.status === httpStatusCodes.BAD_REQUEST) throw err; + + dispatch( + 'setErrorMessage', + { + text: __('An error occurred while committing your changes.'), + action: () => + dispatch('commitChanges').then(() => dispatch('setErrorMessage', null, { root: true })), + actionText: __('Please try again'), + }, + { root: true }, + ); + + window.dispatchEvent(new Event('resize')); }); }; diff --git a/app/assets/stylesheets/framework/secondary_navigation_elements.scss b/app/assets/stylesheets/framework/secondary_navigation_elements.scss index 79f203091f2..bd262b65dc3 100644 --- a/app/assets/stylesheets/framework/secondary_navigation_elements.scss +++ b/app/assets/stylesheets/framework/secondary_navigation_elements.scss @@ -146,11 +146,13 @@ display: inline-block; position: relative; - /* Medium devices (desktops, 992px and up) */ - @include media-breakpoint-up(md) { width: 200px; } + &:not[type='checkbox'] { + /* Medium devices (desktops, 992px and up) */ + @include media-breakpoint-up(md) { width: 200px; } - /* Large devices (large desktops, 1200px and up) */ - @include media-breakpoint-up(lg) { width: 250px; } + /* Large devices (large desktops, 1200px and up) */ + @include media-breakpoint-up(lg) { width: 250px; } + } } @include media-breakpoint-down(sm) { diff --git a/app/assets/stylesheets/pages/alerts_list.scss b/app/assets/stylesheets/pages/alerts_list.scss index 7f817d10ffe..0561e46bcf1 100644 --- a/app/assets/stylesheets/pages/alerts_list.scss +++ b/app/assets/stylesheets/pages/alerts_list.scss @@ -24,14 +24,38 @@ color: $gray-400; } + + // consider adding these stateful variants to @gitlab-ui + // https://gitlab.com/gitlab-org/gitlab-ui/-/merge_requests/1178 + .hover-bg-blue-50:hover { + background-color: $blue-50; + } + + .hover-gl-cursor-pointer:hover { + cursor: pointer; + } + + .hover-gl-border-b-solid:hover { + @include gl-border-b-solid; + } + + .hover-gl-border-blue-200:hover { + border-color: $blue-200; + } + // these styles need to be deleted once GlTable component looks in GitLab same as in @gitlab/ui table { color: $gray-700; tr { + &:focus { + outline: none; + } + td, th { @include gl-p-5; + border: 0; // Remove cell border styling so that we can set border styling per row &.event-count { @include gl-pr-9; @@ -42,9 +66,6 @@ background-color: transparent; font-weight: $gl-font-weight-bold; color: $gl-gray-600; - @include gl-border-b-1; - @include gl-border-b-solid; - border-color: $gray-100; } &:last-child { diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index da8bef405b3..54e3275662b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -20,6 +20,7 @@ class ApplicationController < ActionController::Base include InitializesCurrentUserMode include Impersonation include Gitlab::Logging::CloudflareHelper + include Gitlab::Utils::StrongMemoize before_action :authenticate_user!, except: [:route_not_found] before_action :enforce_terms!, if: :should_enforce_terms? @@ -37,6 +38,10 @@ class ApplicationController < ActionController::Base before_action :check_impersonation_availability before_action :required_signup_info + # Make sure the `auth_user` is memoized so it can be logged, we do this after + # all other before filters that could have set the user. + before_action :auth_user + prepend_around_action :set_current_context around_action :sessionless_bypass_admin_mode!, if: :sessionless_user? @@ -143,10 +148,11 @@ class ApplicationController < ActionController::Base payload[:ua] = request.env["HTTP_USER_AGENT"] payload[:remote_ip] = request.remote_ip + payload[Labkit::Correlation::CorrelationId::LOG_KEY] = Labkit::Correlation::CorrelationId.current_id + payload[:metadata] = @current_context logged_user = auth_user - if logged_user.present? payload[:user_id] = logged_user.try(:id) payload[:username] = logged_user.try(:username) @@ -162,10 +168,12 @@ class ApplicationController < ActionController::Base # (e.g. tokens) to authenticate the user, whereas Devise sets current_user. # def auth_user - if user_signed_in? - current_user - else - try(:authenticated_user) + strong_memoize(:auth_user) do + if user_signed_in? + current_user + else + try(:authenticated_user) + end end end @@ -457,11 +465,16 @@ class ApplicationController < ActionController::Base def set_current_context(&block) Gitlab::ApplicationContext.with_context( - user: -> { auth_user }, - project: -> { @project }, - namespace: -> { @group }, - caller_id: full_action_name, - &block) + # Avoid loading the auth_user again after the request. Otherwise calling + # `auth_user` again would also trigger the Warden callbacks again + user: -> { auth_user if strong_memoized?(:auth_user) }, + project: -> { @project if @project&.persisted? }, + namespace: -> { @group if @group&.persisted? }, + caller_id: full_action_name) do + yield + ensure + @current_context = Labkit::Context.current.to_h + end end def set_locale(&block) diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb index dcd80f4032e..3e7755046cd 100644 --- a/app/controllers/jwt_controller.rb +++ b/app/controllers/jwt_controller.rb @@ -4,7 +4,9 @@ class JwtController < ApplicationController skip_around_action :set_session_storage skip_before_action :authenticate_user! skip_before_action :verify_authenticity_token - before_action :authenticate_project_or_user + + # Add this before other actions, since we want to have the user or project + prepend_before_action :auth_user, :authenticate_project_or_user SERVICES = { Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService @@ -77,7 +79,9 @@ class JwtController < ApplicationController end def auth_user - actor = @authentication_result&.actor - actor.is_a?(User) ? actor : nil + strong_memoize(:auth_user) do + actor = @authentication_result&.actor + actor.is_a?(User) ? actor : nil + end end end diff --git a/app/controllers/projects/alert_management_controller.rb b/app/controllers/projects/alert_management_controller.rb index bcaaeb3c017..0c0a91e136f 100644 --- a/app/controllers/projects/alert_management_controller.rb +++ b/app/controllers/projects/alert_management_controller.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true class Projects::AlertManagementController < Projects::ApplicationController - before_action :ensure_detail_feature_enabled, only: :details before_action :authorize_read_alert_management_alert! before_action do push_frontend_feature_flag(:alert_list_status_filtering_enabled) @@ -14,10 +13,4 @@ class Projects::AlertManagementController < Projects::ApplicationController def details @alert_id = params[:id] end - - private - - def ensure_detail_feature_enabled - render_404 unless Feature.enabled?(:alert_management_detail, project) - end end diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb index 50399a8cfbb..bd966133bb8 100644 --- a/app/controllers/projects/artifacts_controller.rb +++ b/app/controllers/projects/artifacts_controller.rb @@ -10,7 +10,7 @@ class Projects::ArtifactsController < Projects::ApplicationController before_action :authorize_update_build!, only: [:keep] before_action :authorize_destroy_artifacts!, only: [:destroy] before_action :extract_ref_name_and_path - before_action :validate_artifacts!, except: [:index, :download, :destroy] + before_action :validate_artifacts!, except: [:index, :download, :raw, :destroy] before_action :entry, only: [:file] MAX_PER_PAGE = 20 @@ -73,9 +73,11 @@ class Projects::ArtifactsController < Projects::ApplicationController end def raw + return render_404 unless zip_artifact? + path = Gitlab::Ci::Build::Artifacts::Path.new(params[:path]) - send_artifacts_entry(build, path) + send_artifacts_entry(artifacts_file, path) end def keep @@ -138,6 +140,13 @@ class Projects::ArtifactsController < Projects::ApplicationController @artifacts_file ||= build&.artifacts_file_for_type(params[:file_type] || :archive) end + def zip_artifact? + types = HashWithIndifferentAccess.new(Ci::JobArtifact::TYPE_AND_FORMAT_PAIRS) + file_type = params[:file_type] || :archive + + types[file_type] == :zip + end + def entry @entry = build.artifacts_metadata_entry(params[:path]) diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb index bb5b1555dc4..f74b53d68a1 100644 --- a/app/helpers/workhorse_helper.rb +++ b/app/helpers/workhorse_helper.rb @@ -36,8 +36,8 @@ module WorkhorseHelper end # Send an entry from artifacts through Workhorse - def send_artifacts_entry(build, entry) - headers.store(*Gitlab::Workhorse.send_artifacts_entry(build, entry)) + def send_artifacts_entry(file, entry) + headers.store(*Gitlab::Workhorse.send_artifacts_entry(file, entry)) head :ok end diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 5bf3ab95de3..6ebe5227157 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -50,10 +50,10 @@ module Ci metrics: :gzip, metrics_referee: :gzip, network_referee: :gzip, - lsif: :gzip, dotenv: :gzip, cobertura: :gzip, cluster_applications: :gzip, + lsif: :zip, # All these file formats use `raw` as we need to store them uncompressed # for Frontend to fetch the files and do analysis diff --git a/app/models/issue.rb b/app/models/issue.rb index 456d52d27f8..a04ac412940 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -74,6 +74,7 @@ class Issue < ApplicationRecord scope :due_before, ->(date) { where('issues.due_date < ?', date) } scope :due_between, ->(from_date, to_date) { where('issues.due_date >= ?', from_date).where('issues.due_date <= ?', to_date) } scope :due_tomorrow, -> { where(due_date: Date.tomorrow) } + scope :not_authored_by, ->(user) { where.not(author_id: user) } scope :order_due_date_asc, -> { reorder(::Gitlab::Database.nulls_last_order('due_date', 'ASC')) } scope :order_due_date_desc, -> { reorder(::Gitlab::Database.nulls_last_order('due_date', 'DESC')) } @@ -90,6 +91,7 @@ class Issue < ApplicationRecord scope :confidential_only, -> { where(confidential: true) } scope :counts_by_state, -> { reorder(nil).group(:state_id).count } + scope :with_alert_management_alerts, -> { joins(:alert_management_alert) } # An issue can be uniquely identified by project_id and iid # Takes one or more sets of composite IDs, expressed as hash-like records of diff --git a/app/models/service.rb b/app/models/service.rb index f314d119a8e..38cefc2df6e 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -81,6 +81,10 @@ class Service < ApplicationRecord active end + def operating? + active && persisted? + end + def show_active_box? true end diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 3ff4ab354b9..c18af6a267b 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -18,7 +18,7 @@ = _('All features are enabled for blank projects, from templates, or when importing, but you can disable them afterward in the project settings.') = render_if_exists 'projects/new_ci_cd_banner_external_repo' %p - - pages_getting_started_guide = link_to _('Pages getting started guide'), help_page_path("user/project/pages/getting_started_part_two", anchor: "fork-a-project-to-get-started-from"), target: '_blank' + - pages_getting_started_guide = link_to _('Pages getting started guide'), help_page_path("user/project/pages/index", anchor: "getting-started"), target: '_blank' = _('Information about additional Pages templates and how to install them can be found in our %{pages_getting_started_guide}.').html_safe % { pages_getting_started_guide: pages_getting_started_guide } .md = brand_new_project_guidelines diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index b1358be4c9b..3f91bdc4266 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -3,7 +3,7 @@ %h4.prepend-top-0 = @service.title - [true, false].each do |value| - - hide_class = 'd-none' if @service.activated? != value + - hide_class = 'd-none' if @service.operating? != value %span.js-service-active-status{ class: hide_class, data: { value: value.to_s } } = boolean_to_icon value diff --git a/app/views/shared/_group_form.html.haml b/app/views/shared/_group_form.html.haml index 9a65981ed58..019b2ef89a4 100644 --- a/app/views/shared/_group_form.html.haml +++ b/app/views/shared/_group_form.html.haml @@ -6,7 +6,7 @@ .form-group.group-name-holder.col-sm-12 = f.label :name, class: 'label-bold' do = _("Group name") - = f.text_field :name, placeholder: 'My Awesome Group', class: 'form-control input-lg', + = f.text_field :name, placeholder: _('My Awesome Group'), class: 'form-control input-lg', required: true, title: _('Please fill in a descriptive name for your group.'), autofocus: true @@ -22,7 +22,7 @@ - if parent %strong= parent.full_path + '/' = f.hidden_field :parent_id - = f.text_field :path, placeholder: 'my-awesome-group', class: 'form-control js-validate-group-path', + = f.text_field :path, placeholder: _('my-awesome-group'), class: 'form-control js-validate-group-path', autofocus: local_assigns[:autofocus] || false, required: true, pattern: Gitlab::PathRegex::NAMESPACE_FORMAT_REGEX_JS, title: _('Please choose a group URL with no special characters.'), diff --git a/app/views/shared/_milestone_expired.html.haml b/app/views/shared/_milestone_expired.html.haml index a876d605315..48a97a18ca9 100644 --- a/app/views/shared/_milestone_expired.html.haml +++ b/app/views/shared/_milestone_expired.html.haml @@ -1,6 +1,6 @@ - if milestone.expired? and not milestone.closed? - .status-box.status-box-expired.append-bottom-5 = _('Expired') + .status-box.status-box-expired.append-bottom-5= _('Expired') - if milestone.upcoming? - .status-box.status-box-mr-merged.append-bottom-5 = _('Upcoming') + .status-box.status-box-mr-merged.append-bottom-5= _('Upcoming') - if milestone.closed? - .status-box.status-box-closed.append-bottom-5 = _('Closed') + .status-box.status-box-closed.append-bottom-5= _('Closed') diff --git a/app/views/shared/integrations/_index.html.haml b/app/views/shared/integrations/_index.html.haml index 176f899d677..2dbd612ea38 100644 --- a/app/views/shared/integrations/_index.html.haml +++ b/app/views/shared/integrations/_index.html.haml @@ -16,7 +16,7 @@ - activated_label = (integration.activated? ? s_("ProjectService|%{service_title}: status on") : s_("ProjectService|%{service_title}: status off")) % { service_title: integration.title } %tr{ role: 'row' } %td{ role: 'cell', 'aria-colindex': 1, 'aria-label': activated_label } - = boolean_to_icon integration.activated? + = boolean_to_icon integration.operating? %td{ role: 'cell', 'aria-colindex': 2 } = link_to scoped_edit_integration_path(integration), { data: { qa_selector: "#{integration.to_param}_link" } } do %strong= integration.title |