Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-10-19 15:11:29 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-10-19 15:11:29 +0300
commit881435f2a3eeca1b5b544ad7c7510481b1773d1b (patch)
tree34d47e49a899efa730d92d2ea25a31e28be32895 /app
parent91a9a020dafedd084aaa72022f0aa72d14e4f20b (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/invite_members/components/invite_members_modal.vue63
-rw-r--r--app/assets/javascripts/invite_members/components/members_token_select.vue12
-rw-r--r--app/assets/javascripts/invite_members/init_invite_members_modal.js3
-rw-r--r--app/assets/javascripts/super_sidebar/components/nav_item.vue16
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/checks/rebase.stories.js86
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/checks/rebase.vue218
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/merge_checks.stories.js2
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/merge_checks.vue6
-rw-r--r--app/assets/javascripts/vue_merge_request_widget/components/states/ready_to_merge.vue3
-rw-r--r--app/models/ci/build.rb4
-rw-r--r--app/models/ci/build_trace_metadata.rb2
-rw-r--r--app/models/ci/job_artifact.rb2
-rw-r--r--app/models/concerns/token_authenticatable_strategies/base.rb2
-rw-r--r--app/models/group.rb9
-rw-r--r--app/models/system/broadcast_message.rb2
-rw-r--r--app/models/user.rb6
-rw-r--r--app/services/boards/lists/move_service.rb7
-rw-r--r--app/services/packages/npm/create_package_service.rb8
-rw-r--r--app/services/verify_pages_domain_service.rb2
-rw-r--r--app/views/clusters/clusters/_provider_details_form.html.haml28
-rw-r--r--app/views/groups/_invite_members_modal.html.haml6
-rw-r--r--app/views/projects/_invite_members_modal.html.haml6
-rw-r--r--app/workers/bulk_imports/finish_batched_pipeline_worker.rb5
-rw-r--r--app/workers/bulk_imports/pipeline_worker.rb4
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