diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-19 15:11:29 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-19 15:11:29 +0300 |
commit | 881435f2a3eeca1b5b544ad7c7510481b1773d1b (patch) | |
tree | 34d47e49a899efa730d92d2ea25a31e28be32895 /app | |
parent | 91a9a020dafedd084aaa72022f0aa72d14e4f20b (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
24 files changed, 443 insertions, 59 deletions
diff --git a/app/assets/javascripts/invite_members/components/invite_members_modal.vue b/app/assets/javascripts/invite_members/components/invite_members_modal.vue index 509efd31dcd..505612c59da 100644 --- a/app/assets/javascripts/invite_members/components/invite_members_modal.vue +++ b/app/assets/javascripts/invite_members/components/invite_members_modal.vue @@ -1,12 +1,12 @@ <script> -import { GlAlert, GlButton, GlCollapse, GlIcon } from '@gitlab/ui'; +import { GlAlert, GlButton, GlCollapse, GlLink, GlIcon, GlSprintf } from '@gitlab/ui'; import { partition, isString, uniqueId, isEmpty } from 'lodash'; import SafeHtml from '~/vue_shared/directives/safe_html'; import InviteModalBase from 'ee_else_ce/invite_members/components/invite_modal_base.vue'; import Api from '~/api'; import Tracking from '~/tracking'; import { BV_SHOW_MODAL, BV_HIDE_MODAL } from '~/lib/utils/constants'; -import { n__, sprintf } from '~/locale'; +import { n__, s__, sprintf } from '~/locale'; import { memberName, triggerExternalAlert } from 'ee_else_ce/invite_members/utils/member_utils'; import { captureException } from '~/ci/runner/sentry_utils'; import { @@ -31,7 +31,9 @@ export default { GlAlert, GlButton, GlCollapse, + GlLink, GlIcon, + GlSprintf, InviteModalBase, MembersTokenSelect, ModalConfetti, @@ -43,6 +45,17 @@ export default { SafeHtml, }, mixins: [Tracking.mixin({ category: INVITE_MEMBER_MODAL_TRACKING_CATEGORY })], + inject: { + isCurrentUserAdmin: { + default: false, + }, + isEmailSignupEnabled: { + default: true, + }, + newUsersUrl: { + default: '', + }, + }, props: { id: { type: String, @@ -122,6 +135,9 @@ export default { isCelebration() { return this.mode === 'celebrate'; }, + isTextForAdmin() { + return this.isCurrentUserAdmin && Boolean(this.newUsersUrl); + }, modalTitle() { return this.$options.labels.modal[this.mode].title; }, @@ -131,6 +147,11 @@ export default { labelIntroText() { return this.$options.labels[this.inviteTo][this.mode].introText; }, + labelSearchField() { + return this.isEmailSignupEnabled + ? this.$options.labels.searchField + : s__('InviteMembersModal|Username'); + }, isEmptyInvites() { return Boolean(this.newUsersToInvite.length); }, @@ -144,6 +165,14 @@ export default { this.errorList.length, ); }, + signupDisabledText() { + return s__( + "InviteMembersModal|Administrators can %{linkStart}add new users by email manually%{linkEnd}. After they've been added, you can invite them to this group with their username.", + ); + }, + signupDisabledTitle() { + return s__('InviteMembersModal|Inviting users by email is disabled'); + }, showUserLimitNotification() { return !isEmpty(this.usersLimitDataset.alertVariant); }, @@ -173,8 +202,13 @@ export default { count: this.errorsExpanded.length, }); }, + formGroupDescriptionText() { + return this.isEmailSignupEnabled + ? this.$options.labels.placeHolder + : s__('InviteMembersModal|Select members'); + }, formGroupDescription() { - return this.invalidFeedbackMessage ? null : this.$options.labels.placeHolder; + return this.invalidFeedbackMessage ? null : this.formGroupDescriptionText; }, }, watch: { @@ -224,7 +258,7 @@ export default { this.$root.$emit(BV_HIDE_MODAL, this.modalId); }, showEmptyInvitesAlert() { - this.invalidFeedbackMessage = this.$options.labels.placeHolder; + this.invalidFeedbackMessage = this.formGroupDescriptionText; this.shouldShowEmptyInvitesAlert = true; this.$refs.alerts.focus(); }, @@ -345,7 +379,7 @@ export default { :default-access-level="defaultAccessLevel" :help-link="helpLink" :label-intro-text="labelIntroText" - :label-search-field="$options.labels.searchField" + :label-search-field="labelSearchField" :form-group-description="formGroupDescription" :invalid-feedback-message="invalidFeedbackMessage" :is-loading="isLoading" @@ -429,6 +463,24 @@ export default { </gl-button> </template> </gl-alert> + <gl-alert + v-if="!isEmailSignupEnabled" + id="signup-disabled-alert" + :dismissible="false" + :title="signupDisabledTitle" + class="gl-mb-4" + variant="warning" + data-testid="email-signup-disabled-alert" + > + <gl-sprintf :message="signupDisabledText"> + <template #link="{ content }"> + <gl-link v-if="isTextForAdmin" :href="newUsersUrl" target="_blank">{{ + content + }}</gl-link> + <span v-else>{{ content }}</span> + </template> + </gl-sprintf> + </gl-alert> <user-limit-notification v-else-if="showUserLimitNotification" class="gl-mb-5" @@ -447,6 +499,7 @@ export default { v-model="newUsersToInvite" class="gl-mb-2" aria-labelledby="empty-invites-alert" + :can-use-email-token="isEmailSignupEnabled" :input-id="inputId" :exception-state="exceptionState" :users-filter="usersFilter" diff --git a/app/assets/javascripts/invite_members/components/members_token_select.vue b/app/assets/javascripts/invite_members/components/members_token_select.vue index 8493787f075..0be04b7af35 100644 --- a/app/assets/javascripts/invite_members/components/members_token_select.vue +++ b/app/assets/javascripts/invite_members/components/members_token_select.vue @@ -21,6 +21,11 @@ export default { GlSprintf, }, props: { + canUseEmailToken: { + type: Boolean, + required: false, + default: true, + }, placeholder: { type: String, required: false, @@ -68,6 +73,10 @@ export default { }, computed: { emailIsValid() { + if (!this.canUseEmailToken) { + return false; + } + const regex = /^\S+@\S+$/; return this.originalInput.match(regex) !== null; @@ -137,9 +146,8 @@ export default { username: token.username, avatar_url: token.avatar_url, })); - this.loading = false; }) - .catch(() => { + .finally(() => { this.loading = false; }); }, SEARCH_DELAY), diff --git a/app/assets/javascripts/invite_members/init_invite_members_modal.js b/app/assets/javascripts/invite_members/init_invite_members_modal.js index 41ed0179364..8dfe697e2cb 100644 --- a/app/assets/javascripts/invite_members/init_invite_members_modal.js +++ b/app/assets/javascripts/invite_members/init_invite_members_modal.js @@ -25,6 +25,9 @@ export default (function initInviteMembersModal() { name: 'InviteMembersModalRoot', provide: { name: el.dataset.name, + newUsersUrl: el.dataset.newUsersUrl, + isCurrentUserAdmin: parseBoolean(el.dataset.isCurrentUserAdmin), + isEmailSignupEnabled: parseBoolean(el.dataset.isSignupEnabled), }, render: (createElement) => createElement(InviteMembersModal, { diff --git a/app/assets/javascripts/super_sidebar/components/nav_item.vue b/app/assets/javascripts/super_sidebar/components/nav_item.vue index 5416f86abeb..ff48b8d92cc 100644 --- a/app/assets/javascripts/super_sidebar/components/nav_item.vue +++ b/app/assets/javascripts/super_sidebar/components/nav_item.vue @@ -70,14 +70,16 @@ export default { return { isMouseIn: false, canClickPinButton: false, - pillCount: this.item.pill_count, }; }, computed: { + pillData() { + return this.item.pill_count; + }, hasPill() { return ( - Number.isFinite(this.pillCount) || - (typeof this.pillCount === 'string' && this.pillCount !== '') + Number.isFinite(this.pillData) || + (typeof this.pillData === 'string' && this.pillData !== '') ); }, isPinnable() { @@ -193,7 +195,11 @@ export default { }, updatePillValue({ value, itemId }) { if (this.item.id === itemId) { - this.pillCount = value; + // https://gitlab.com/gitlab-org/gitlab/-/issues/428246 + // fixing this linting issue is causing the pills not to async update + // + // eslint-disable-next-line vue/no-mutating-props + this.item.pill_count = value; } }, }, @@ -258,7 +264,7 @@ export default { 'hide-on-focus-or-hover--target transition-opacity-on-hover--target': isPinnable, }" > - {{ pillCount }} + {{ pillData }} </gl-badge> </span> </component> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/checks/rebase.stories.js b/app/assets/javascripts/vue_merge_request_widget/components/checks/rebase.stories.js new file mode 100644 index 00000000000..7458a2503e8 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/checks/rebase.stories.js @@ -0,0 +1,86 @@ +import createMockApollo from 'helpers/mock_apollo_helper'; +import rebaseStateQuery from '../../queries/states/rebase.query.graphql'; +import Rebase from './rebase.vue'; + +const service = { + rebase: () => new Promise(() => {}), +}; + +const defaultRender = ({ apolloProvider, check, mr, canCreatePipelineInTargetProject }) => ({ + components: { Rebase }, + apolloProvider, + provide: { + canCreatePipelineInTargetProject, + }, + data() { + return { service, mr: { ...mr, targetProjectFullPath: 'gitlab-org/gitlab' }, check }; + }, + template: '<rebase :mr="mr" :service="service" :check="check" />', +}); + +const Template = ({ + failed, + pushToSourceBranch, + rebaseInProgress, + onlyAllowMergeIfPipelineSucceeds, + canCreatePipelineInTargetProject, +}) => { + const requestHandlers = [ + [ + rebaseStateQuery, + () => + Promise.resolve({ + data: { + project: { + id: '1', + mergeRequest: { + id: '2', + rebaseInProgress, + targetBranch: 'main', + userPermissions: { + pushToSourceBranch, + }, + pipelines: { + nodes: [ + { + id: '1', + project: { + id: '2', + fullPath: 'gitlab/gitlab', + }, + }, + ], + }, + }, + }, + }, + }), + ], + ]; + const apolloProvider = createMockApollo(requestHandlers); + + return defaultRender({ + apolloProvider, + check: { + failureReason: 'Needs rebasing', + identifier: 'rebase', + result: failed ? 'failed' : 'passed', + }, + mr: { onlyAllowMergeIfPipelineSucceeds }, + canCreatePipelineInTargetProject, + }); +}; + +export const Default = Template.bind({}); +Default.args = { + failed: true, + pushToSourceBranch: true, + rebaseInProgress: false, + onlyAllowMergeIfPipelineSucceeds: false, + canCreatePipelineInTargetProject: false, +}; + +export default { + title: 'vue_merge_request_widget/merge_checks/rebase', + component: Rebase, +}; diff --git a/app/assets/javascripts/vue_merge_request_widget/components/checks/rebase.vue b/app/assets/javascripts/vue_merge_request_widget/components/checks/rebase.vue new file mode 100644 index 00000000000..823a30c7063 --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/checks/rebase.vue @@ -0,0 +1,218 @@ +<script> +import { GlModal, GlLink } from '@gitlab/ui'; +import { s__, __ } from '~/locale'; +import { helpPagePath } from '~/helpers/help_page_helper'; +import { createAlert } from '~/alert'; +import toast from '~/vue_shared/plugins/global_toast'; +import simplePoll from '~/lib/utils/simple_poll'; +import mergeRequestQueryVariablesMixin from '../../mixins/merge_request_query_variables'; +import rebaseQuery from '../../queries/states/rebase.query.graphql'; +import eventHub from '../../event_hub'; +import ActionButtons from '../action_buttons.vue'; +import MergeChecksMessage from './message.vue'; + +export default { + name: 'MergeChecksRebase', + components: { + GlModal, + GlLink, + MergeChecksMessage, + ActionButtons, + }, + mixins: [mergeRequestQueryVariablesMixin], + apollo: { + state: { + query: rebaseQuery, + variables() { + return this.mergeRequestQueryVariables; + }, + update: (data) => data.project.mergeRequest, + }, + }, + inject: { + canCreatePipelineInTargetProject: { + default: false, + }, + }, + props: { + check: { + type: Object, + required: true, + }, + mr: { + type: Object, + required: false, + default: () => ({}), + }, + service: { + type: Object, + required: false, + default: () => ({}), + }, + }, + data() { + return { + state: {}, + isMakingRequest: false, + }; + }, + computed: { + isLoading() { + return this.$apollo.queries.state.loading; + }, + rebaseInProgress() { + return this.state.rebaseInProgress; + }, + showRebaseWithoutPipeline() { + return ( + !this.mr.onlyAllowMergeIfPipelineSucceeds || + (this.mr.onlyAllowMergeIfPipelineSucceeds && this.mr.allowMergeOnSkippedPipeline) + ); + }, + isForkMergeRequest() { + return this.mr.sourceProjectFullPath !== this.mr.targetProjectFullPath; + }, + isLatestPipelineCreatedInTargetProject() { + const latestPipeline = this.state.pipelines.nodes[0]; + + return latestPipeline?.project?.fullPath === this.mr.targetProjectFullPath; + }, + shouldShowSecurityWarning() { + return ( + this.canCreatePipelineInTargetProject && + this.isForkMergeRequest && + !this.isLatestPipelineCreatedInTargetProject + ); + }, + tertiaryActionsButtons() { + if (this.check.result === 'success') return []; + + return [ + { + text: s__('mrWidget|Rebase'), + loading: this.isMakingRequest || this.rebaseInProgress, + testId: 'standard-rebase-button', + onClick: () => this.tryRebase(), + }, + this.showRebaseWithoutPipeline && { + text: s__('mrWidget|Rebase without pipeline'), + loading: this.isMakingRequest || this.rebaseInProgress, + testId: 'rebase-without-ci-button', + onClick: () => this.rebaseWithoutCi(), + }, + ].filter((b) => b); + }, + }, + methods: { + rebase({ skipCi = false } = {}) { + this.isMakingRequest = true; + + this.service + .rebase({ skipCi }) + .then(() => simplePoll(this.checkRebaseStatus)) + .catch((error) => { + this.isMakingRequest = false; + + if (!error.response?.data?.merge_error) { + createAlert({ + message: __('Something went wrong. Please try again.'), + }); + } + }); + }, + rebaseWithoutCi() { + return this.rebase({ skipCi: true }); + }, + tryRebase() { + if (this.shouldShowSecurityWarning) { + this.$refs.modal.show(); + } else { + this.rebase(); + } + }, + checkRebaseStatus(continuePolling, stopPolling) { + this.service + .poll() + .then((res) => res.data) + .then((res) => { + if (res.rebase_in_progress || res.should_be_rebased) { + continuePolling(); + } else { + this.isMakingRequest = false; + + if (!res.merge_error?.length) { + toast(__('Rebase completed')); + } + + eventHub.$emit('MRWidgetRebaseSuccess'); + stopPolling(); + } + }) + .catch(() => { + this.isMakingRequest = false; + createAlert({ + message: __('Something went wrong. Please try again.'), + }); + stopPolling(); + }); + }, + }, + modal: { + id: 'rebase-security-risk-modal', + title: s__('mrWidget|Are you sure you want to rebase?'), + actionPrimary: { + text: s__('mrWidget|Rebase'), + attributes: { + variant: 'danger', + }, + }, + actionCancel: { + text: __('Cancel'), + attributes: { + variant: 'default', + }, + }, + }, + runPipelinesInTheParentProjectHelpPath: helpPagePath( + '/ci/pipelines/merge_request_pipelines.html', + { + anchor: 'run-pipelines-in-the-parent-project', + }, + ), +}; +</script> + +<template> + <merge-checks-message :check="check"> + <action-buttons v-if="!isLoading" :tertiary-buttons="tertiaryActionsButtons" /> + <gl-modal + ref="modal" + :modal-id="$options.modal.id" + :title="$options.modal.title" + :action-primary="$options.modal.actionPrimary" + :action-cancel="$options.modal.actionCancel" + @primary="rebase" + > + <p> + {{ + s__( + 'Pipelines|Rebasing creates a pipeline that runs code originating from a forked project merge request. Consequently there are potential security implications, such as the exposure of CI variables.', + ) + }} + </p> + <p> + {{ + s__( + "Pipelines|You should review the code thoroughly before running this pipeline with the parent project's CI/CD resources.", + ) + }} + </p> + <p> + {{ s__('Pipelines|If you are unsure, ask a project maintainer to review it for you.') }} + </p> + <gl-link :href="$options.runPipelinesInTheParentProjectHelpPath" target="_blank"> + {{ s__('Pipelines|More Information') }} + </gl-link> + </gl-modal> + </merge-checks-message> +</template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.stories.js b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.stories.js index 1c57226f887..a9745f3214c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.stories.js +++ b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.stories.js @@ -15,7 +15,7 @@ const defaultRender = (apolloProvider) => ({ components: { MergeChecks }, apolloProvider, data() { - return { mr: { conflictResolutionPath: 'https://gitlab.com' } }; + return { service: {}, mr: { conflictResolutionPath: 'https://gitlab.com' } }; }, template: '<merge-checks :mr="mr" />', }); diff --git a/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue index fa84c0a4a6f..5652b81386f 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue @@ -8,6 +8,7 @@ import BoldText from './bold_text.vue'; const COMPONENTS = { conflicts: () => import('./checks/conflicts.vue'), + rebase: () => import('./checks/rebase.vue'), default: () => import('./checks/message.vue'), }; @@ -35,6 +36,10 @@ export default { type: Object, required: true, }, + service: { + type: Object, + required: true, + }, }, data() { return { @@ -122,6 +127,7 @@ export default { }" :check="check" :mr="mr" + :service="service" /> </div> </div> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue index ac434c5be4e..ac7e44364d8 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue @@ -730,6 +730,9 @@ export default { class="mr-ready-merge-related-links gl-display-inline" /> </li> + <li v-if="state.autoMergeEnabled" class="gl-line-height-normal"> + {{ s__('mrWidget|Auto-merge enabled') }} + </li> </ul> </div> </div> diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index d2cf9058976..a4724fd7c02 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -729,7 +729,7 @@ module Ci end def artifacts_expired? - artifacts_expire_at && artifacts_expire_at < Time.current + artifacts_expire_at&.past? end def artifacts_expire_in @@ -745,7 +745,7 @@ module Ci def has_expired_locked_archive_artifacts? locked_artifacts? && - artifacts_expire_at.present? && artifacts_expire_at < Time.current + artifacts_expire_at&.past? end def has_expiring_archive_artifacts? diff --git a/app/models/ci/build_trace_metadata.rb b/app/models/ci/build_trace_metadata.rb index c5ad3d19425..525cb08f2ca 100644 --- a/app/models/ci/build_trace_metadata.rb +++ b/app/models/ci/build_trace_metadata.rb @@ -33,7 +33,7 @@ module Ci return false unless archival_attempts_available? return true unless last_archival_attempt_at - last_archival_attempt_at + backoff < Time.current + (last_archival_attempt_at + backoff).past? end def archival_attempts_available? diff --git a/app/models/ci/job_artifact.rb b/app/models/ci/job_artifact.rb index 2a346f97958..fe4437a4ad6 100644 --- a/app/models/ci/job_artifact.rb +++ b/app/models/ci/job_artifact.rb @@ -306,7 +306,7 @@ module Ci end def expired? - expire_at.present? && expire_at < Time.current + expire_at.present? && expire_at.past? end def expiring? diff --git a/app/models/concerns/token_authenticatable_strategies/base.rb b/app/models/concerns/token_authenticatable_strategies/base.rb index d0085b60d98..b25ee434484 100644 --- a/app/models/concerns/token_authenticatable_strategies/base.rb +++ b/app/models/concerns/token_authenticatable_strategies/base.rb @@ -65,7 +65,7 @@ module TokenAuthenticatableStrategies return false unless expirable? && token_expiration_enforced? exp = expires_at(instance) - !!exp && Time.current > exp + !!exp && exp.past? end def expirable? diff --git a/app/models/group.rb b/app/models/group.rb index c83dd24e98e..919b80ccffb 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -671,15 +671,6 @@ class Group < Namespace members.count end - # Returns all users that are members of projects - # belonging to the current group or sub-groups - def project_users_with_descendants - User - .joins(projects: :group) - .where(namespaces: { id: self_and_descendants.select(:id) }) - .allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/417455") - end - # Return the highest access level for a user # # A special case is handled here when the user is a GitLab admin diff --git a/app/models/system/broadcast_message.rb b/app/models/system/broadcast_message.rb index 06f0115ade6..d959a6339a4 100644 --- a/app/models/system/broadcast_message.rb +++ b/app/models/system/broadcast_message.rb @@ -117,7 +117,7 @@ module System end def ended? - ends_at < Time.current + ends_at.past? end def now? diff --git a/app/models/user.rb b/app/models/user.rb index 4034677509f..5b6d9f3b6e8 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1081,7 +1081,7 @@ class User < MainClusterwide::ApplicationRecord def otp_secret_expired? return true unless otp_secret_expires_at - otp_secret_expires_at < Time.current + otp_secret_expires_at.past? end def update_otp_secret! @@ -1446,7 +1446,7 @@ class User < MainClusterwide::ApplicationRecord if !Gitlab.config.ldap.enabled false elsif ldap_user? - !last_credential_check_at || (last_credential_check_at + ldap_sync_time) < Time.current + !last_credential_check_at || (last_credential_check_at + ldap_sync_time).past? else false end @@ -2087,7 +2087,7 @@ class User < MainClusterwide::ApplicationRecord end def password_expired? - !!(password_expires_at && password_expires_at < Time.current) + !!(password_expires_at && password_expires_at.past?) end def password_expired_if_applicable? diff --git a/app/services/boards/lists/move_service.rb b/app/services/boards/lists/move_service.rb index 4bb7b4dbc6d..4715f1276e3 100644 --- a/app/services/boards/lists/move_service.rb +++ b/app/services/boards/lists/move_service.rb @@ -22,8 +22,11 @@ module Boards attr_reader :board, :old_position, :new_position def valid_move? - new_position.present? && new_position != old_position && - new_position >= 0 && new_position <= board.lists.movable.last.position + new_position.present? && new_position != old_position && new_position.between?(0, max_position) + end + + def max_position + board.lists.movable.maximum(:position) end def reorder_intermediate_lists diff --git a/app/services/packages/npm/create_package_service.rb b/app/services/packages/npm/create_package_service.rb index d599cecc8da..0f0dc297e9a 100644 --- a/app/services/packages/npm/create_package_service.rb +++ b/app/services/packages/npm/create_package_service.rb @@ -12,6 +12,7 @@ module Packages return error('Version is empty.', 400) if version.blank? return error('Attachment data is empty.', 400) if attachment['data'].blank? return error('Package already exists.', 403) if current_package_exists? + return error('Package protected.', 403) if current_package_protected? return error('File is too large.', 400) if file_size_exceeded? package = try_obtain_lease do @@ -56,6 +57,13 @@ module Packages .exists? end + def current_package_protected? + return false if Feature.disabled?(:packages_protected_packages, project) + + user_project_authorization_access_level = current_user.max_member_access_for_project(project.id) + project.package_protection_rules.push_protected_from?(access_level: user_project_authorization_access_level, package_name: name, package_type: :npm) + end + def name params[:name] end diff --git a/app/services/verify_pages_domain_service.rb b/app/services/verify_pages_domain_service.rb index 59c73aa929c..f5dfe13539b 100644 --- a/app/services/verify_pages_domain_service.rb +++ b/app/services/verify_pages_domain_service.rb @@ -79,7 +79,7 @@ class VerifyPagesDomainService < BaseService # A domain is only expired until `disable!` has been called def expired? - domain.enabled_until && domain.enabled_until < Time.current + domain.enabled_until&.past? end def dns_record_present? diff --git a/app/views/clusters/clusters/_provider_details_form.html.haml b/app/views/clusters/clusters/_provider_details_form.html.haml index 4b7164f9845..dfb97263c54 100644 --- a/app/views/clusters/clusters/_provider_details_form.html.haml +++ b/app/views/clusters/clusters/_provider_details_form.html.haml @@ -1,35 +1,35 @@ = gitlab_ui_form_for cluster, url: update_cluster_url_path, html: { class: 'js-provider-details gl-show-field-errors', role: 'form' }, as: :cluster do |field| .form-group - - copy_name_btn = deprecated_clipboard_button(text: cluster.name, title: s_('ClusterIntegration|Copy Kubernetes cluster name'), - class: 'input-group-text btn-default') if cluster.read_only_kubernetes_platform_fields? = field.label :name, s_('ClusterIntegration|Kubernetes cluster name'), class: 'label-bold required' .input-group.gl-field-error-anchor = field.text_field :name, class: 'form-control js-select-on-focus cluster-name', required: true, title: s_('ClusterIntegration|Cluster name is required.'), - readonly: cluster.read_only_kubernetes_platform_fields?, - append: copy_name_btn + readonly: cluster.read_only_kubernetes_platform_fields? + - if cluster.read_only_kubernetes_platform_fields? + .input-group-append + = clipboard_button(text: cluster.name, title: s_('ClusterIntegration|Copy Kubernetes cluster name'), variant: :default, category: :primary, size: :medium) = field.fields_for :platform_kubernetes, platform do |platform_field| .form-group - - copy_api_url = deprecated_clipboard_button(text: platform.api_url, title: s_('ClusterIntegration|Copy API URL'), - class: 'input-group-text btn-default') if cluster.read_only_kubernetes_platform_fields? = platform_field.label :api_url, s_('ClusterIntegration|API URL'), class: 'label-bold required' .input-group.gl-field-error-anchor = platform_field.text_field :api_url, class: 'form-control js-select-on-focus', required: true, title: s_('ClusterIntegration|API URL should be a valid http/https url.'), - readonly: cluster.read_only_kubernetes_platform_fields?, - append: copy_api_url + readonly: cluster.read_only_kubernetes_platform_fields? + - if cluster.read_only_kubernetes_platform_fields? + .input-group-append + = clipboard_button(text: platform.api_url, title: s_('ClusterIntegration|Copy API URL'), variant: :default, category: :primary, size: :medium) .form-group - - copy_ca_cert_btn = deprecated_clipboard_button(text: platform.ca_cert, title: s_('ClusterIntegration|Copy CA Certificate'), - class: 'input-group-text btn-default') if cluster.read_only_kubernetes_platform_fields? = platform_field.label :ca_cert, s_('ClusterIntegration|CA Certificate'), class: 'label-bold' - .input-group.gl-field-error-anchor - = platform_field.text_area :ca_cert, class: 'form-control js-select-on-focus', rows: '10', + .input-group.gl-field-error-anchor.markdown-code-block + = platform_field.text_area :ca_cert, class: 'gl-rounded-top-right-base! gl-rounded-bottom-right-base! form-control js-select-on-focus', rows: '10', readonly: cluster.read_only_kubernetes_platform_fields?, - placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)'), - append: copy_ca_cert_btn + placeholder: s_('ClusterIntegration|Certificate Authority bundle (PEM format)') + - if cluster.read_only_kubernetes_platform_fields? + %copy-code + = clipboard_button(text: platform.ca_cert, title: s_('ClusterIntegration|Copy CA Certificate'), variant: :default, category: :primary, size: :medium, class: 'copy-code') .form-group = platform_field.label :token, s_('ClusterIntegration|Enter new Service Token'), class: 'label-bold required' diff --git a/app/views/groups/_invite_members_modal.html.haml b/app/views/groups/_invite_members_modal.html.haml index cd3327ba9ec..d53190948fd 100644 --- a/app/views/groups/_invite_members_modal.html.haml +++ b/app/views/groups/_invite_members_modal.html.haml @@ -3,4 +3,8 @@ .js-invite-members-modal{ data: { is_project: 'false', access_levels: group.access_level_roles.to_json, reload_page_on_submit: current_path?('group_members#index').to_s, - help_link: help_page_url('user/permissions') }.merge(common_invite_modal_dataset(group)).merge(users_filter_data(group)) } + help_link: help_page_url('user/permissions'), + is_signup_enabled: signup_enabled?.to_s, + new_users_url: new_admin_user_url, + is_current_user_admin: current_user&.admin?.to_s, + }.merge(common_invite_modal_dataset(group)).merge(users_filter_data(group)) } diff --git a/app/views/projects/_invite_members_modal.html.haml b/app/views/projects/_invite_members_modal.html.haml index a1b0bdd6c56..8713cb4990a 100644 --- a/app/views/projects/_invite_members_modal.html.haml +++ b/app/views/projects/_invite_members_modal.html.haml @@ -3,4 +3,8 @@ .js-invite-members-modal{ data: { is_project: 'true', access_levels: ProjectMember.permissible_access_level_roles(current_user, project).to_json, reload_page_on_submit: current_path?('project_members#index').to_s, - help_link: help_page_url('user/permissions') }.merge(common_invite_modal_dataset(project)).merge(users_filter_data(project.group)) } + help_link: help_page_url('user/permissions'), + is_signup_enabled: signup_enabled?.to_s, + new_users_url: new_admin_user_url, + is_current_user_admin: current_user&.admin?.to_s, + }.merge(common_invite_modal_dataset(project)).merge(users_filter_data(project.group)) } diff --git a/app/workers/bulk_imports/finish_batched_pipeline_worker.rb b/app/workers/bulk_imports/finish_batched_pipeline_worker.rb index b1f3757e058..b953f8ab786 100644 --- a/app/workers/bulk_imports/finish_batched_pipeline_worker.rb +++ b/app/workers/bulk_imports/finish_batched_pipeline_worker.rb @@ -27,11 +27,6 @@ module BulkImports else tracker.finish! end - - ensure - # This is needed for in-flight migrations. - # It will be remove in https://gitlab.com/gitlab-org/gitlab/-/issues/426299 - ::BulkImports::EntityWorker.perform_async(tracker.entity.id) if job_version.nil? end private diff --git a/app/workers/bulk_imports/pipeline_worker.rb b/app/workers/bulk_imports/pipeline_worker.rb index 24185f43795..2f57c4579cc 100644 --- a/app/workers/bulk_imports/pipeline_worker.rb +++ b/app/workers/bulk_imports/pipeline_worker.rb @@ -34,10 +34,6 @@ module BulkImports fail_tracker(StandardError.new(message)) unless pipeline_tracker.finished? || pipeline_tracker.skipped? end end - ensure - # This is needed for in-flight migrations. - # It will be remove in https://gitlab.com/gitlab-org/gitlab/-/issues/426299 - ::BulkImports::EntityWorker.perform_async(entity_id) if job_version.nil? end private |