diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-27 15:10:33 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-07-27 15:10:33 +0300 |
commit | fdf32113c3924f7faec91101282fc28ec42fc869 (patch) | |
tree | 388fdb9982d5ae80c8bc9b9bdcc0dde98cd6ead9 /app | |
parent | 5add82515889cf332b65bbf59394079222dc66b3 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
20 files changed, 299 insertions, 206 deletions
diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js index d1e5e4eea13..aceae188b73 100644 --- a/app/assets/javascripts/lib/utils/constants.js +++ b/app/assets/javascripts/lib/utils/constants.js @@ -15,6 +15,7 @@ export const DATETIME_RANGE_TYPES = { export const BV_SHOW_MODAL = 'bv::show::modal'; export const BV_HIDE_MODAL = 'bv::hide::modal'; export const BV_HIDE_TOOLTIP = 'bv::hide::tooltip'; +export const BV_SHOW_TOOLTIP = 'bv::show::tooltip'; export const BV_DROPDOWN_SHOW = 'bv::dropdown::show'; export const BV_DROPDOWN_HIDE = 'bv::dropdown::hide'; diff --git a/app/assets/javascripts/repository/components/blob_content_viewer.vue b/app/assets/javascripts/repository/components/blob_content_viewer.vue index a34ed065323..bdb78bab909 100644 --- a/app/assets/javascripts/repository/components/blob_content_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_content_viewer.vue @@ -21,7 +21,13 @@ import projectInfoQuery from '../queries/project_info.query.graphql'; import getRefMixin from '../mixins/get_ref'; import userInfoQuery from '../queries/user_info.query.graphql'; import applicationInfoQuery from '../queries/application_info.query.graphql'; -import { DEFAULT_BLOB_INFO, TEXT_FILE_TYPE, LFS_STORAGE, LEGACY_FILE_TYPES } from '../constants'; +import { + DEFAULT_BLOB_INFO, + TEXT_FILE_TYPE, + LFS_STORAGE, + LEGACY_FILE_TYPES, + CODEOWNERS_FILE_NAME, +} from '../constants'; import BlobButtonGroup from './blob_button_group.vue'; import ForkSuggestion from './fork_suggestion.vue'; import { loadViewer } from './blob_viewers'; @@ -32,6 +38,7 @@ export default { BlobButtonGroup, BlobContent, GlLoadingIcon, + CodeownersValidation: () => import('ee_component/blob/components/codeowners_validation.vue'), GlButton, ForkSuggestion, WebIdeLink, @@ -79,7 +86,7 @@ export default { const queryVariables = { projectPath: this.projectPath, filePath: this.path, - ref: this.originalBranch || this.ref, + ref: this.currentRef, refType: this.refType?.toUpperCase() || null, shouldFetchRawText: true, }; @@ -171,6 +178,12 @@ export default { return nodes[0] || {}; }, + currentRef() { + return this.originalBranch || this.ref; + }, + isCodeownersFile() { + return this.path.includes(CODEOWNERS_FILE_NAME); + }, viewer() { const { richViewer, simpleViewer } = this.blobInfo; return this.activeViewerType === RICH_BLOB_VIEWER ? richViewer : simpleViewer; @@ -402,6 +415,12 @@ export default { :fork-path="forkPath" @cancel="setForkTarget(null)" /> + <codeowners-validation + v-if="isCodeownersFile" + :current-ref="currentRef" + :project-path="projectPath" + :file-path="path" + /> <blob-content v-if="!blobViewer" class="js-syntax-highlight" diff --git a/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue b/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue index 014f1abc121..9a8bb8e4aa6 100644 --- a/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue +++ b/app/assets/javascripts/repository/components/blob_viewers/image_viewer.vue @@ -8,7 +8,7 @@ export default { }, data() { return { - url: this.blob.rawPath, + url: this.blob.externalStorageUrl || this.blob.rawPath, alt: this.blob.name, }; }, diff --git a/app/assets/javascripts/repository/constants.js b/app/assets/javascripts/repository/constants.js index b711f671850..4327b237c8c 100644 --- a/app/assets/javascripts/repository/constants.js +++ b/app/assets/javascripts/repository/constants.js @@ -114,3 +114,5 @@ export const POLLING_INTERVAL_BACKOFF = 2; export const CONFLICTS_MODAL_ID = 'fork-sync-conflicts-modal'; export const FORK_UPDATED_EVENT = 'fork:updated'; + +export const CODEOWNERS_FILE_NAME = 'CODEOWNERS'; diff --git a/app/assets/javascripts/search/sidebar/components/checkbox_filter.vue b/app/assets/javascripts/search/sidebar/components/checkbox_filter.vue index feff3f77dd2..bca049e56c7 100644 --- a/app/assets/javascripts/search/sidebar/components/checkbox_filter.vue +++ b/app/assets/javascripts/search/sidebar/components/checkbox_filter.vue @@ -27,7 +27,7 @@ export default { }, }, computed: { - ...mapState(['query', 'useNewNavigation']), + ...mapState(['query', 'useSidebarNavigation']), ...mapGetters(['queryLanguageFilters']), dataFilters() { return Object.values(this.filtersData?.filters || []); @@ -69,7 +69,9 @@ export default { <template> <div class="gl-mx-5"> - <h5 class="gl-mt-0" :class="{ 'gl-font-sm': useNewNavigation }">{{ filtersData.header }}</h5> + <h5 class="gl-mt-0" :class="{ 'gl-font-sm': useSidebarNavigation }"> + {{ filtersData.header }} + </h5> <gl-form-checkbox-group v-model="selectedFilter"> <gl-form-checkbox v-for="f in dataFilters" diff --git a/app/assets/javascripts/search/sidebar/components/confidentiality_filter/index.vue b/app/assets/javascripts/search/sidebar/components/confidentiality_filter/index.vue index 7909aa9234b..312092b9904 100644 --- a/app/assets/javascripts/search/sidebar/components/confidentiality_filter/index.vue +++ b/app/assets/javascripts/search/sidebar/components/confidentiality_filter/index.vue @@ -10,7 +10,7 @@ export default { RadioFilter, }, computed: { - ...mapState(['useNewNavigation']), + ...mapState(['useSidebarNavigation']), }, confidentialFilterData, HR_DEFAULT_CLASSES, diff --git a/app/assets/javascripts/search/sidebar/components/issues_filters.vue b/app/assets/javascripts/search/sidebar/components/issues_filters.vue index 3eb025327a2..5fd8b6418d7 100644 --- a/app/assets/javascripts/search/sidebar/components/issues_filters.vue +++ b/app/assets/javascripts/search/sidebar/components/issues_filters.vue @@ -28,7 +28,7 @@ export default { }, mixins: [glFeatureFlagsMixin()], computed: { - ...mapState(['urlQuery', 'sidebarDirty', 'useNewNavigation']), + ...mapState(['urlQuery', 'sidebarDirty', 'useSidebarNavigation']), ...mapGetters(['currentScope']), showReset() { return this.urlQuery.state || this.urlQuery.confidential || this.urlQuery.labels; @@ -69,12 +69,12 @@ export default { <template> <form class="issue-filters gl-px-5 gl-pt-0" @submit.prevent="applyQueryWithTracking"> - <hr v-if="!useNewNavigation" :class="hrClasses" /> + <hr v-if="!useSidebarNavigation" :class="hrClasses" /> <status-filter v-if="showStatusFilter" class="gl-mb-5" /> - <hr v-if="!useNewNavigation" :class="hrClasses" /> + <hr v-if="!useSidebarNavigation" :class="hrClasses" /> <confidentiality-filter v-if="showConfidentialityFilter" class="gl-mb-5" /> <hr - v-if="!useNewNavigation && showConfidentialityFilter && showLabelFilter" + v-if="!useSidebarNavigation && showConfidentialityFilter && showLabelFilter" :class="hrClasses" /> <label-filter v-if="showLabelFilter" /> diff --git a/app/assets/javascripts/search/sidebar/components/language_filter/checkbox_filter.vue b/app/assets/javascripts/search/sidebar/components/language_filter/checkbox_filter.vue index b820ca837bc..b42fe9185cb 100644 --- a/app/assets/javascripts/search/sidebar/components/language_filter/checkbox_filter.vue +++ b/app/assets/javascripts/search/sidebar/components/language_filter/checkbox_filter.vue @@ -27,7 +27,7 @@ export default { }, }, computed: { - ...mapState(['query', 'useNewNavigation']), + ...mapState(['query', 'useSidebarNavigation']), ...mapGetters(['queryLanguageFilters']), dataFilters() { return Object.values(this.filtersData?.filters || []); diff --git a/app/assets/javascripts/search/sidebar/components/language_filter/index.vue b/app/assets/javascripts/search/sidebar/components/language_filter/index.vue index c10b14bd116..e5560dd5b55 100644 --- a/app/assets/javascripts/search/sidebar/components/language_filter/index.vue +++ b/app/assets/javascripts/search/sidebar/components/language_filter/index.vue @@ -36,7 +36,7 @@ export default { reset: s__('GlobalSearch|Reset filters'), }, computed: { - ...mapState(['aggregations', 'sidebarDirty', 'useNewNavigation']), + ...mapState(['aggregations', 'sidebarDirty', 'useSidebarNavigation']), ...mapGetters([ 'languageAggregationBuckets', 'currentUrlQueryHasLanguageFilters', @@ -120,8 +120,8 @@ export default { class="gl-m-5 gl-my-0 language-filter-checkbox" @submit.prevent="submitQuery" > - <hr v-if="!useNewNavigation" :class="dividerClassesTop" /> - <h5 class="gl-mt-0 gl-mb-5" :class="{ 'gl-font-sm': useNewNavigation }"> + <hr v-if="!useSidebarNavigation" :class="dividerClassesTop" /> + <h5 class="gl-mt-0 gl-mb-5" :class="{ 'gl-font-sm': useSidebarNavigation }"> {{ $options.languageFilterData.header }} </h5> <div @@ -153,7 +153,7 @@ export default { </gl-button> </div> <div v-if="!aggregations.error"> - <hr v-if="!useNewNavigation" :class="dividerClassesBottom" /> + <hr v-if="!useSidebarNavigation" :class="dividerClassesBottom" /> <div class="gl-display-flex gl-align-items-center gl-justify-content-space-between gl-mt-4"> <gl-button category="primary" diff --git a/app/assets/javascripts/search/sidebar/components/radio_filter.vue b/app/assets/javascripts/search/sidebar/components/radio_filter.vue index 10ece1b82eb..8ad403ab31b 100644 --- a/app/assets/javascripts/search/sidebar/components/radio_filter.vue +++ b/app/assets/javascripts/search/sidebar/components/radio_filter.vue @@ -16,7 +16,7 @@ export default { }, }, computed: { - ...mapState(['query', 'useNewNavigation']), + ...mapState(['query', 'useSidebarNavigation']), ...mapGetters(['currentScope']), ANY() { return this.filterData.filters.ANY; @@ -56,7 +56,7 @@ export default { <template> <div> - <h5 class="gl-mt-0 gl-mb-5" :class="{ 'gl-font-sm': useNewNavigation }"> + <h5 class="gl-mt-0 gl-mb-5" :class="{ 'gl-font-sm': useSidebarNavigation }"> {{ filterData.header }} </h5> <gl-form-radio-group v-model="selectedFilter"> diff --git a/app/assets/javascripts/search/sidebar/components/results_filters.vue b/app/assets/javascripts/search/sidebar/components/results_filters.vue index a9addb87f7b..88e434cf99e 100644 --- a/app/assets/javascripts/search/sidebar/components/results_filters.vue +++ b/app/assets/javascripts/search/sidebar/components/results_filters.vue @@ -16,7 +16,7 @@ export default { ConfidentialityFilter, }, computed: { - ...mapState(['urlQuery', 'sidebarDirty', 'useNewNavigation']), + ...mapState(['urlQuery', 'sidebarDirty', 'useSidebarNavigation']), ...mapGetters(['currentScope']), showReset() { return this.urlQuery.state || this.urlQuery.confidential; @@ -39,7 +39,7 @@ export default { <template> <form class="gl-pt-5 gl-md-pt-0" @submit.prevent="applyQuery"> - <hr v-if="!useNewNavigation" :class="hrClasses" /> + <hr v-if="!useSidebarNavigation" :class="hrClasses" /> <status-filter v-if="showStatusFilter" /> <confidentiality-filter v-if="showConfidentialityFilter" /> <div class="gl-display-flex gl-align-items-center gl-mt-4 gl-px-5"> diff --git a/app/assets/javascripts/search/sidebar/components/status_filter/index.vue b/app/assets/javascripts/search/sidebar/components/status_filter/index.vue index 494d75db6ce..010cfbad590 100644 --- a/app/assets/javascripts/search/sidebar/components/status_filter/index.vue +++ b/app/assets/javascripts/search/sidebar/components/status_filter/index.vue @@ -10,7 +10,7 @@ export default { RadioFilter, }, computed: { - ...mapState(['useNewNavigation']), + ...mapState(['useSidebarNavigation']), }, statusFilterData, HR_DEFAULT_CLASSES, diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss index 2e88b45d646..613e504c771 100644 --- a/app/assets/stylesheets/framework/files.scss +++ b/app/assets/stylesheets/framework/files.scss @@ -263,6 +263,11 @@ span.idiff { } } +.file-validation { + // we use $gray-light variable instead of utility class, because it's value is dynamic per color theme + background-color: $gray-light; +} + .blob-content-holder .file-actions { @include media-breakpoint-down(sm) { .btn { diff --git a/app/assets/stylesheets/framework/new_card.scss b/app/assets/stylesheets/framework/new_card.scss index 48a834858c6..411f5300120 100644 --- a/app/assets/stylesheets/framework/new_card.scss +++ b/app/assets/stylesheets/framework/new_card.scss @@ -95,6 +95,10 @@ // Table adjustments @mixin new-card-table-adjustments { tbody > tr { + &:first-of-type > td[data-label] { + @include gl-border-t-0; + } + > td[data-label] { @include gl-border-left-0; @include gl-border-l-none; @@ -119,8 +123,15 @@ table.b-table-stacked-sm, table.b-table-stacked-md { - @include gl-mt-n1; - @include gl-mb-n2; + @include gl-mb-0; + + tr:first-of-type th { + @include gl-border-t-0; + } + + tr:last-of-type td { + @include gl-border-b-0; + } } table.gl-table.b-table.b-table-stacked-sm { diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index da70361743b..1ce852d4f71 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -5,6 +5,7 @@ module Ci prepend Ci::BulkInsertableTags include Ci::Metadatable include Ci::Contextable + include Ci::Deployable include TokenAuthenticatable include AfterCommitQueue include Presentable @@ -34,7 +35,6 @@ module Ci DEPLOYMENT_NAMES = %w[deploy release rollout].freeze - has_one :deployment, as: :deployable, class_name: 'Deployment', inverse_of: :deployable has_one :pending_state, class_name: 'Ci::BuildPendingState', foreign_key: :build_id, inverse_of: :build has_one :queuing_entry, class_name: 'Ci::PendingBuild', foreign_key: :build_id, inverse_of: :build has_one :runtime_metadata, class_name: 'Ci::RunningBuild', foreign_key: :build_id, inverse_of: :build @@ -327,7 +327,6 @@ module Ci after_transition any => [:success] do |build| build.run_after_commit do - BuildSuccessWorker.perform_async(id) PagesWorker.perform_async(:deploy, id) if build.pages_generator? end end @@ -345,18 +344,6 @@ module Ci end end end - - # Synchronize Deployment Status - # Please note that the data integirty is not assured because we can't use - # a database transaction due to DB decomposition. - after_transition do |build, transition| - next if transition.loopback? - next unless build.project - - build.run_after_commit do - build.deployment&.sync_status_with(build) - end - end end def self.build_matchers(project) @@ -428,15 +415,6 @@ module Ci action? && !archived? && (manual? || scheduled? || retryable?) end - def outdated_deployment? - strong_memoize(:outdated_deployment) do - deployment_job? && - project.ci_forward_deployment_enabled? && - (!project.ci_forward_deployment_rollback_allowed? || incomplete?) && - deployment&.older_than_last_successful_deployment? - end - end - def schedulable? self.when == 'delayed' && options[:start_in].present? end @@ -478,94 +456,10 @@ module Ci Gitlab::Ci::Build::Prerequisite::Factory.new(self).unmet end - def persisted_environment - return unless has_environment_keyword? - - strong_memoize(:persisted_environment) do - # This code path has caused N+1s in the past, since environments are only indirectly - # associated to builds and pipelines; see https://gitlab.com/gitlab-org/gitlab/-/issues/326445 - # We therefore batch-load them to prevent dormant N+1s until we found a proper solution. - BatchLoader.for(expanded_environment_name).batch(key: project_id) do |names, loader, args| - Environment.where(name: names, project: args[:key]).find_each do |environment| - loader.call(environment.name, environment) - end - end - end - end - - def persisted_environment=(environment) - strong_memoize(:persisted_environment) { environment } - end - - # If build.persisted_environment is a BatchLoader, we need to remove - # the method proxy in order to clone into new item here - # https://github.com/exAspArk/batch-loader/issues/31 - def actual_persisted_environment - persisted_environment.respond_to?(:__sync) ? persisted_environment.__sync : persisted_environment - end - - def expanded_environment_name - return unless has_environment_keyword? - - strong_memoize(:expanded_environment_name) do - # We're using a persisted expanded environment name in order to avoid - # variable expansion per request. - if metadata&.expanded_environment_name.present? - metadata.expanded_environment_name - else - ExpandVariables.expand(environment, -> { simple_variables.sort_and_expand_all }) - end - end - end - - def expanded_kubernetes_namespace - return unless has_environment_keyword? - - namespace = options.dig(:environment, :kubernetes, :namespace) - - if namespace.present? - strong_memoize(:expanded_kubernetes_namespace) do - ExpandVariables.expand(namespace, -> { simple_variables }) - end - end - end - - def has_environment_keyword? - environment.present? - end - - def deployment_job? - has_environment_keyword? && environment_action == 'start' - end - - def stops_environment? - has_environment_keyword? && environment_action == 'stop' - end - - def environment_action - options.fetch(:environment, {}).fetch(:action, 'start') if options - end - - def environment_tier_from_options - options.dig(:environment, :deployment_tier) if options - end - - def environment_tier - environment_tier_from_options || persisted_environment.try(:tier) - end - def triggered_by?(current_user) user == current_user end - def on_stop - options&.dig(:environment, :on_stop) - end - - def stop_action_successful? - success? - end - ## # All variables, including persisted environment variables. # @@ -1033,19 +927,6 @@ module Ci job_artifacts.all_reports end - # Virtual deployment status depending on the environment status. - def deployment_status - return unless deployment_job? - - if success? - return successful_deployment_status - elsif failed? - return :failed - end - - :creating - end - # Consider this object to have a structural integrity problems def doom! transaction do @@ -1206,31 +1087,11 @@ module Ci strong_memoize(:build_data) { Gitlab::DataBuilder::Build.build(self) } end - def successful_deployment_status - if deployment&.last? - :last - else - :out_of_date - end - end - def job_artifacts_for_types(report_types) # Use select to leverage cached associations and avoid N+1 queries job_artifacts.select { |artifact| artifact.file_type.in?(report_types) } end - def environment_url - options&.dig(:environment, :url) || persisted_environment&.external_url - end - - def environment_status - strong_memoize(:environment_status) do - if has_environment_keyword? && merge_request - EnvironmentStatus.new(project, persisted_environment, merge_request, pipeline.sha) - end - end - end - def has_expiring_artifacts? artifacts_expire_at.present? && artifacts_expire_at > Time.current end diff --git a/app/models/concerns/ci/deployable.rb b/app/models/concerns/ci/deployable.rb new file mode 100644 index 00000000000..ffdb38f76ac --- /dev/null +++ b/app/models/concerns/ci/deployable.rb @@ -0,0 +1,159 @@ +# frozen_string_literal: true + +# rubocop:disable Gitlab/StrongMemoizeAttr +module Ci + module Deployable + extend ActiveSupport::Concern + + included do + has_one :deployment, as: :deployable, class_name: 'Deployment', inverse_of: :deployable + + state_machine :status do + after_transition any => [:success] do |job| + job.run_after_commit do + Environments::StopJobSuccessWorker.perform_async(id) + end + end + + # Synchronize Deployment Status + # Please note that the data integirty is not assured because we can't use + # a database transaction due to DB decomposition. + after_transition do |job, transition| + next if transition.loopback? + next unless job.project + + job.run_after_commit do + job.deployment&.sync_status_with(job) + end + end + end + end + + def outdated_deployment? + strong_memoize(:outdated_deployment) do + deployment_job? && + project.ci_forward_deployment_enabled? && + (!project.ci_forward_deployment_rollback_allowed? || incomplete?) && + deployment&.older_than_last_successful_deployment? + end + end + + # Virtual deployment status depending on the environment status. + def deployment_status + return unless deployment_job? + + if success? + return successful_deployment_status + elsif failed? + return :failed + end + + :creating + end + + def successful_deployment_status + if deployment&.last? + :last + else + :out_of_date + end + end + + def persisted_environment + return unless has_environment_keyword? + + strong_memoize(:persisted_environment) do + # This code path has caused N+1s in the past, since environments are only indirectly + # associated to builds and pipelines; see https://gitlab.com/gitlab-org/gitlab/-/issues/326445 + # We therefore batch-load them to prevent dormant N+1s until we found a proper solution. + BatchLoader.for(expanded_environment_name).batch(key: project_id) do |names, loader, args| + Environment.where(name: names, project: args[:key]).find_each do |environment| + loader.call(environment.name, environment) + end + end + end + end + + def persisted_environment=(environment) + strong_memoize(:persisted_environment) { environment } + end + + # If build.persisted_environment is a BatchLoader, we need to remove + # the method proxy in order to clone into new item here + # https://github.com/exAspArk/batch-loader/issues/31 + def actual_persisted_environment + persisted_environment.respond_to?(:__sync) ? persisted_environment.__sync : persisted_environment + end + + def expanded_environment_name + return unless has_environment_keyword? + + strong_memoize(:expanded_environment_name) do + # We're using a persisted expanded environment name in order to avoid + # variable expansion per request. + if metadata&.expanded_environment_name.present? + metadata.expanded_environment_name + else + ExpandVariables.expand(environment, -> { simple_variables.sort_and_expand_all }) + end + end + end + + def expanded_kubernetes_namespace + return unless has_environment_keyword? + + namespace = options.dig(:environment, :kubernetes, :namespace) + + if namespace.present? # rubocop:disable Style/GuardClause + strong_memoize(:expanded_kubernetes_namespace) do + ExpandVariables.expand(namespace, -> { simple_variables }) + end + end + end + + def has_environment_keyword? + environment.present? + end + + def deployment_job? + has_environment_keyword? && environment_action == 'start' + end + + def stops_environment? + has_environment_keyword? && environment_action == 'stop' + end + + def environment_action + options.fetch(:environment, {}).fetch(:action, 'start') if options + end + + def environment_tier_from_options + options.dig(:environment, :deployment_tier) if options + end + + def environment_tier + environment_tier_from_options || persisted_environment.try(:tier) + end + + def environment_url + options&.dig(:environment, :url) || persisted_environment&.external_url + end + + def environment_status + strong_memoize(:environment_status) do + if has_environment_keyword? && merge_request + EnvironmentStatus.new(project, persisted_environment, merge_request, pipeline.sha) + end + end + end + + def on_stop + options&.dig(:environment, :on_stop) + end + + def stop_action_successful? + success? + end + end +end +# rubocop:enable Gitlab/StrongMemoizeAttr diff --git a/app/views/admin/applications/index.html.haml b/app/views/admin/applications/index.html.haml index e32a50e252d..063045033b6 100644 --- a/app/views/admin/applications/index.html.haml +++ b/app/views/admin/applications/index.html.haml @@ -1,52 +1,51 @@ - page_title s_('AdminArea|Instance OAuth applications') -%h1.page-title.gl-font-size-h-display - = s_('AdminArea|Instance OAuth applications') -%p.light - - docs_link_path = help_page_path('integration/oauth_provider') - - docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer nofollow">'.html_safe % { url: docs_link_path } - = s_('AdminArea|Manage applications for your instance that can use GitLab as an %{docs_link_start}OAuth provider%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: '</a>'.html_safe } += render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card js-toggle-container' }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body gl-px-0' }) do |c| + - c.with_header do + .gl-new-card-title-wrapper.gl-flex-direction-column + %h3.gl-new-card-title + = s_('AdminArea|Instance OAuth applications') + .gl-new-card-count + = sprite_icon('applications', css_class: 'gl-mr-2') + = @applications.size + %p.gl-new-card-description + - docs_link_path = help_page_path('integration/oauth_provider') + - docs_link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer nofollow">'.html_safe % { url: docs_link_path } + = s_('AdminArea|Manage applications for your instance that can use GitLab as an %{docs_link_start}OAuth provider%{docs_link_end}.').html_safe % { docs_link_start: docs_link_start, docs_link_end: '</a>'.html_safe } -- if @applications.empty? - %section.empty-state.gl-text-center.gl-display-flex.gl-flex-direction-column - .svg-content.svg-150 - = image_tag 'illustrations/empty-state/empty-admin-apps-md.svg', class: 'gl-max-w-full' - - .gl-max-w-full.gl-m-auto - %h1.h4.gl-font-size-h-display= s_('AdminArea|No applications found') - = render Pajamas::ButtonComponent.new(href: new_admin_application_path, variant: :confirm, button_options: { data: { qa_selector: 'new_application_button' } }) do + .gl-new-card-actions + = render Pajamas::ButtonComponent.new(size: :small, href: new_admin_application_path, button_options: { data: { qa_selector: 'new_application_button' } }) do = _('New application') + - c.with_body do + - if @applications.empty? + %section.empty-state.gl-my-5.gl-text-center.gl-display-flex.gl-flex-direction-column + .svg-content.svg-150 + = image_tag 'illustrations/empty-state/empty-admin-apps-md.svg', class: 'gl-max-w-full' -- else - %hr - = render Pajamas::ButtonComponent.new(href: new_admin_application_path, variant: :confirm, button_options: { data: { qa_selector: 'new_application_button' } }) do - = _('New application') - - .table-responsive - %table.b-table.gl-table.gl-w-full{ role: 'table' } - %thead - %tr - %th - = _('Name') - %th - = _('Callback URL') - %th - = _('Trusted') - %th - = _('Confidential') - %th - %th - %tbody.oauth-applications - - @applications.each do |application| - %tr{ id: "application_#{application.id}" } - %td= link_to application.name, admin_application_path(application) - %td= application.redirect_uri - %td= application.trusted? ? _('Yes'): _('No') - %td= application.confidential? ? _('Yes'): _('No') - %td - = render Pajamas::ButtonComponent.new(href: edit_admin_application_path(application), variant: :link) do - = _('Edit') - %td= render 'delete_form', application: application + .gl-max-w-full.gl-m-auto + %h1.h4.gl-font-size-h-display= s_('AdminArea|No applications found') + %p.gl-text-secondary.gl-mt-3= s_('AdminArea|Manage applications for your instance that can use GitLab as an OAuth provider, start by creating a new one above.') + - else + .table-holder + %table.table.b-table.gl-table.b-table-stacked-md{ role: 'table' } + %thead + %tr + %th= _('Name') + %th= _('Callback URL') + %th= _('Trusted') + %th= _('Confidential') + %th= _('Actions') + %tbody.oauth-applications + - @applications.each do |application| + %tr{ id: "application_#{application.id}" } + %td{ data: { label: _('Name') } }= link_to application.name, admin_application_path(application) + %td{ data: { label: _('Callback URL') } }= application.redirect_uri + %td{ data: { label: _('Trusted') } }= application.trusted? ? _('Yes'): _('No') + %td{ data: { label: _('Confidential') } }= application.confidential? ? _('Yes'): _('No') + %td{ data: { label: _('Actions') } } + = render Pajamas::ButtonComponent.new(href: edit_admin_application_path(application), size: :small, button_options: { class: 'gl-mr-3' }) do + = _('Edit') + = render 'delete_form', application: application = paginate @applications, theme: 'gitlab' diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index e41c85d1550..89bb1d11d1d 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -2712,6 +2712,15 @@ :weight: 1 :idempotent: true :tags: [] +- :name: environments_stop_job_success + :worker_name: Environments::StopJobSuccessWorker + :feature_category: :continuous_delivery + :has_external_dependencies: false + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] - :name: error_tracking_issue_link :worker_name: ErrorTrackingIssueLinkWorker :feature_category: :error_tracking diff --git a/app/workers/build_success_worker.rb b/app/workers/build_success_worker.rb index 247105d2a1a..f5baa220715 100644 --- a/app/workers/build_success_worker.rb +++ b/app/workers/build_success_worker.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +# Deprecated and will be removed in 17.0. +# Use `Environments::StopJobSuccessWorker` instead. class BuildSuccessWorker # rubocop:disable Scalability/IdempotentWorker include ApplicationWorker diff --git a/app/workers/environments/stop_job_success_worker.rb b/app/workers/environments/stop_job_success_worker.rb new file mode 100644 index 00000000000..cc7d83512f3 --- /dev/null +++ b/app/workers/environments/stop_job_success_worker.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Environments + class StopJobSuccessWorker + include ApplicationWorker + + data_consistency :delayed + idempotent! + feature_category :continuous_delivery + + def perform(job_id, _params = {}) + Ci::Build.find_by_id(job_id).try do |build| + stop_environment(build) if build.stops_environment? && build.stop_action_successful? + end + end + + private + + def stop_environment(build) + build.persisted_environment.fire_state_event(:stop_complete) + end + end +end |