diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-07 15:09:49 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-07 15:09:49 +0000 |
commit | 84f9f0cb8137637708a41152347e7754c1e9fb83 (patch) | |
tree | 6db9d8931bdb3c5b932b36345373936e2a543126 /app | |
parent | 75f809a2ff829574ab91628407993187d55e14a4 (diff) | |
download | gitlab-ce-84f9f0cb8137637708a41152347e7754c1e9fb83.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
10 files changed, 166 insertions, 37 deletions
diff --git a/app/assets/javascripts/analytics/shared/components/metric_popover.vue b/app/assets/javascripts/analytics/shared/components/metric_popover.vue index 8d90e7b2392..373a7fac6f7 100644 --- a/app/assets/javascripts/analytics/shared/components/metric_popover.vue +++ b/app/assets/javascripts/analytics/shared/components/metric_popover.vue @@ -1,5 +1,6 @@ <script> import { GlPopover, GlLink, GlIcon } from '@gitlab/ui'; +import { METRIC_POPOVER_LABEL } from '../constants'; export default { name: 'MetricPopover', @@ -19,34 +20,34 @@ export default { }, }, computed: { - metricLinks() { - return this.metric.links?.filter((link) => !link.docs_link) || []; + metricLink() { + return this.metric.links?.find((link) => !link.docs_link); }, docsLink() { return this.metric.links?.find((link) => link.docs_link); }, }, + metricPopoverLabel: METRIC_POPOVER_LABEL, }; </script> <template> - <gl-popover :target="target" placement="bottom"> + <gl-popover :target="target" placement="top"> <template #title> - <span class="gl-display-block gl-text-left" data-testid="metric-label">{{ - metric.label - }}</span> + <div + class="gl-display-flex gl-justify-content-space-between gl-text-right gl-py-1 gl-align-items-center" + > + <span data-testid="metric-label">{{ metric.label }}</span> + <gl-link + v-if="metricLink" + :href="metricLink.url" + class="gl-font-sm gl-font-weight-normal" + data-testid="metric-link" + >{{ $options.metricPopoverLabel }} + <gl-icon name="chart" /> + </gl-link> + </div> </template> - <div - v-for="(link, idx) in metricLinks" - :key="`link-${idx}`" - class="gl-display-flex gl-justify-content-space-between gl-text-right gl-py-1" - data-testid="metric-link" - > - <span>{{ link.label }}</span> - <gl-link :href="link.url" class="gl-font-sm"> - {{ link.name }} - </gl-link> - </div> <span v-if="metric.description" data-testid="metric-description">{{ metric.description }}</span> <gl-link v-if="docsLink" diff --git a/app/assets/javascripts/analytics/shared/constants.js b/app/assets/javascripts/analytics/shared/constants.js index a82633035b5..7ced658f483 100644 --- a/app/assets/javascripts/analytics/shared/constants.js +++ b/app/assets/javascripts/analytics/shared/constants.js @@ -13,6 +13,8 @@ export const dateFormats = { month: 'mmmm', }; +export const METRIC_POPOVER_LABEL = s__('ValueStreamAnalytics|View details'); + export const KEY_METRICS = { LEAD_TIME: 'lead_time', CYCLE_TIME: 'cycle_time', diff --git a/app/assets/javascripts/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue b/app/assets/javascripts/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue index 84c29e48114..7368d1a3a91 100644 --- a/app/assets/javascripts/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue +++ b/app/assets/javascripts/ci/pipeline_editor/components/file_nav/pipeline_editor_file_nav.vue @@ -52,7 +52,7 @@ export default { }; </script> <template> - <div class="gl-mb-4"> + <div class="gl-mb-4 gl-display-flex gl-flex-wrap gl-gap-3"> <gl-button v-if="showFileTreeToggle" id="file-tree-toggle" diff --git a/app/assets/javascripts/editor/schema/ci.json b/app/assets/javascripts/editor/schema/ci.json index 324acb177b0..57477a993c5 100644 --- a/app/assets/javascripts/editor/schema/ci.json +++ b/app/assets/javascripts/editor/schema/ci.json @@ -417,6 +417,20 @@ "type": "object", "additionalProperties": false, "properties": { + "component": { + "description": "Local path to component directory or full path to external component directory.", + "type": "string", + "format": "uri-reference" + } + }, + "required": [ + "component" + ] + }, + { + "type": "object", + "additionalProperties": false, + "properties": { "remote": { "description": "URL to a `yaml`/`yml` template file using HTTP/HTTPS.", "type": "string", @@ -1955,4 +1969,4 @@ "additionalProperties": false } } -}
\ No newline at end of file +} diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue index 9b669024a8b..f3d392a0ec4 100644 --- a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue +++ b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue @@ -1,23 +1,22 @@ <script> -import { s__ } from '~/locale'; +import { GlButton, GlModal, GlModalDirective } from '@gitlab/ui'; import { createAlert } from '~/flash'; import branchRulesQuery from 'ee_else_ce/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql'; +import { expandSection } from '~/settings_panels'; +import { scrollToElement } from '~/lib/utils/common_utils'; import BranchRule from './components/branch_rule.vue'; - -export const i18n = { - queryError: s__( - 'ProtectedBranch|An error occurred while loading branch rules. Please try again.', - ), - emptyState: s__( - 'ProtectedBranch|Protected branches, merge request approvals, and status checks will appear here once configured.', - ), -}; +import { I18N, PROTECTED_BRANCHES_ANCHOR, BRANCH_PROTECTION_MODAL_ID } from './constants'; export default { name: 'BranchRules', - i18n, + i18n: I18N, components: { BranchRule, + GlButton, + GlModal, + }, + directives: { + GlModal: GlModalDirective, }, apollo: { branchRules: { @@ -36,20 +35,27 @@ export default { }, }, inject: { - projectPath: { - default: '', - }, + projectPath: { default: '' }, }, data() { return { branchRules: [], }; }, + methods: { + showProtectedBranches() { + // Protected branches section is on the same page as the branch rules section. + expandSection(this.$options.protectedBranchesAnchor); + scrollToElement(this.$options.protectedBranchesAnchor); + }, + }, + modalId: BRANCH_PROTECTION_MODAL_ID, + protectedBranchesAnchor: PROTECTED_BRANCHES_ANCHOR, }; </script> <template> - <div class="settings-content"> + <div class="settings-content gl-mb-0"> <branch-rule v-for="(rule, index) in branchRules" :key="`${rule.name}-${index}`" @@ -61,6 +67,21 @@ export default { :matching-branches-count="rule.matchingBranchesCount" /> - <span v-if="!branchRules.length" data-testid="empty">{{ $options.i18n.emptyState }}</span> + <div v-if="!branchRules.length" data-testid="empty">{{ $options.i18n.emptyState }}</div> + + <gl-button v-gl-modal="$options.modalId" class="gl-mt-5" category="secondary" variant="info">{{ + $options.i18n.addBranchRule + }}</gl-button> + + <gl-modal + :ref="$options.modalId" + :modal-id="$options.modalId" + :title="$options.i18n.addBranchRule" + :ok-title="$options.i18n.createProtectedBranch" + @ok="showProtectedBranches" + > + <p>{{ $options.i18n.branchRuleModalDescription }}</p> + <p>{{ $options.i18n.branchRuleModalContent }}</p> + </gl-modal> </div> </template> diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/constants.js b/app/assets/javascripts/projects/settings/repository/branch_rules/constants.js new file mode 100644 index 00000000000..4413d8eab4e --- /dev/null +++ b/app/assets/javascripts/projects/settings/repository/branch_rules/constants.js @@ -0,0 +1,22 @@ +import { s__ } from '~/locale'; + +export const I18N = { + queryError: s__( + 'ProtectedBranch|An error occurred while loading branch rules. Please try again.', + ), + emptyState: s__( + 'ProtectedBranch|After you configure a protected branch, merge request approval, or status check, it appears here.', + ), + addBranchRule: s__('BranchRules|Add branch rule'), + branchRuleModalDescription: s__( + 'BranchRules|To create a branch rule, you first need to create a protected branch.', + ), + branchRuleModalContent: s__( + 'BranchRules|After a protected branch is created, it will show up in the list as a branch rule.', + ), + createProtectedBranch: s__('BranchRules|Create protected branch'), +}; + +export const PROTECTED_BRANCHES_ANCHOR = '#js-protected-branches-settings'; + +export const BRANCH_PROTECTION_MODAL_ID = 'addBranchRuleModal'; diff --git a/app/assets/stylesheets/page_bundles/settings.scss b/app/assets/stylesheets/page_bundles/settings.scss index 9037eb7ae62..8978b8d798b 100644 --- a/app/assets/stylesheets/page_bundles/settings.scss +++ b/app/assets/stylesheets/page_bundles/settings.scss @@ -71,6 +71,7 @@ animation: collapseMaxHeight 300ms ease-out; // Keep the section from expanding when we scroll over it pointer-events: none; + margin-bottom: $gl-spacing-scale-5; .settings.expanded & { max-height: none; @@ -101,7 +102,6 @@ display: block; height: 1px; overflow: hidden; - margin-top: 20px; } .sub-section { diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 0dca5b18a24..b5622959c75 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -141,8 +141,11 @@ module Ci before_save :set_size, if: :file_changed? after_save :store_file_in_transaction!, unless: :store_after_commit? + after_commit :store_file_after_transaction!, on: [:create, :update], if: :store_after_commit? + after_destroy_commit :log_destroy + validates :job, presence: true validates :file_format, presence: true, unless: :trace?, on: :create validate :validate_file_format!, unless: :trace?, on: :create @@ -384,6 +387,10 @@ module Ci # Use job.project to avoid extra DB query for project job.project.pending_delete? end + + def log_destroy + Gitlab::Ci::Artifacts::Logger.log_deleted(self, __method__) + end end end diff --git a/app/models/environment.rb b/app/models/environment.rb index 7d99f10822d..7febafc2cca 100644 --- a/app/models/environment.rb +++ b/app/models/environment.rb @@ -74,7 +74,11 @@ class Environment < ApplicationRecord # Currently, the tier presence is validaed for newly created environments. # After the `BackfillEnvironmentTiers` background migration has been completed, we should remove `on: :create`. # See https://gitlab.com/gitlab-org/gitlab/-/issues/385253. - validates :tier, presence: true, on: :create + # Todo: Remove along with FF `validate_environment_tier_presence`. + validates :tier, presence: true, on: :create, unless: :validate_environment_tier_present? + + validates :tier, presence: true, if: :validate_environment_tier_present? + validate :safe_external_url validate :merge_request_not_changed @@ -600,6 +604,10 @@ class Environment < ApplicationRecord self.class.tiers[:other] end end + + def validate_environment_tier_present? + Feature.enabled?(:validate_environment_tier_presence, self.project) + end end Environment.prepend_mod_with('Environment') diff --git a/app/services/ci/components/fetch_service.rb b/app/services/ci/components/fetch_service.rb new file mode 100644 index 00000000000..45abb415174 --- /dev/null +++ b/app/services/ci/components/fetch_service.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Ci + module Components + class FetchService + include Gitlab::Utils::StrongMemoize + + TEMPLATE_FILE = 'template.yml' + + COMPONENT_PATHS = [ + ::Gitlab::Ci::Components::InstancePath + ].freeze + + def initialize(address:, current_user:) + @address = address + @current_user = current_user + end + + def execute + unless component_path_class + return ServiceResponse.error( + message: "#{error_prefix} the component path is not supported", + reason: :unsupported_path) + end + + component_path = component_path_class.new(address: address, content_filename: TEMPLATE_FILE) + content = component_path.fetch_content!(current_user: current_user) + + if content.present? + ServiceResponse.success(payload: { content: content, path: component_path }) + else + ServiceResponse.error(message: "#{error_prefix} content not found", reason: :content_not_found) + end + rescue Gitlab::Access::AccessDeniedError + ServiceResponse.error( + message: "#{error_prefix} project does not exist or you don't have sufficient permissions", + reason: :not_allowed) + end + + private + + attr_reader :current_user, :address + + def component_path_class + COMPONENT_PATHS.find { |klass| klass.match?(address) } + end + strong_memoize_attr :component_path_class + + def error_prefix + "component '#{address}' -" + end + end + end +end |