diff options
Diffstat (limited to 'app')
40 files changed, 366 insertions, 416 deletions
diff --git a/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue b/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue index 4ec41381045..f0a41a5949e 100644 --- a/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue +++ b/app/assets/javascripts/ci/runner/admin_new_runner/admin_new_runner_app.vue @@ -60,7 +60,7 @@ export default { <hr aria-hidden="true" /> - <h2 class="gl-font-weight-normal gl-font-lg gl-my-5"> + <h2 class="gl-font-size-h2 gl-my-5"> {{ s__('Runners|Platform') }} </h2> <runner-platforms-radio-group v-model="platform" /> diff --git a/app/assets/javascripts/ci/runner/components/runner_create_form.vue b/app/assets/javascripts/ci/runner/components/runner_create_form.vue index d6db15a1996..040e42fa938 100644 --- a/app/assets/javascripts/ci/runner/components/runner_create_form.vue +++ b/app/assets/javascripts/ci/runner/components/runner_create_form.vue @@ -83,13 +83,13 @@ export default { if (errors?.length) { this.$emit('error', new Error(errors.join(' '))); + this.saving = false; } else { this.onSuccess(runner); } } catch (error) { captureException({ error, component: this.$options.name }); this.$emit('error', error); - } finally { this.saving = false; } }, @@ -103,7 +103,7 @@ export default { <gl-form @submit.prevent="onSubmit"> <runner-form-fields v-model="runner" /> - <div class="gl-display-flex"> + <div class="gl-display-flex gl-mt-6"> <gl-button type="submit" variant="confirm" class="js-no-auto-disable" :loading="saving"> {{ s__('Runners|Create runner') }} </gl-button> diff --git a/app/assets/javascripts/ci/runner/components/runner_form_fields.vue b/app/assets/javascripts/ci/runner/components/runner_form_fields.vue index 880d217e344..180c41e7ed6 100644 --- a/app/assets/javascripts/ci/runner/components/runner_form_fields.vue +++ b/app/assets/javascripts/ci/runner/components/runner_form_fields.vue @@ -1,5 +1,14 @@ <script> -import { GlFormGroup, GlFormCheckbox, GlFormInput, GlIcon, GlLink, GlSprintf } from '@gitlab/ui'; +import { isEqual } from 'lodash'; +import { + GlFormGroup, + GlFormCheckbox, + GlFormInput, + GlIcon, + GlLink, + GlSprintf, + GlSkeletonLoader, +} from '@gitlab/ui'; import { helpPagePath } from '~/helpers/help_page_helper'; import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED, PROJECT_TYPE } from '../constants'; @@ -12,6 +21,7 @@ export default { GlIcon, GlLink, GlSprintf, + GlSkeletonLoader, RunnerMaintenanceNoteField: () => import('ee_component/ci/runner/components/runner_maintenance_note_field.vue'), }, @@ -21,12 +31,15 @@ export default { default: null, required: false, }, + loading: { + type: Boolean, + default: false, + required: false, + }, }, data() { return { - model: { - ...this.value, - }, + model: null, }; }, computed: { @@ -35,6 +48,15 @@ export default { }, }, watch: { + value: { + handler(newVal, oldVal) { + // update only when values change, avoids infinite loop + if (!isEqual(newVal, oldVal)) { + this.model = { ...newVal }; + } + }, + immediate: true, + }, model: { handler() { this.$emit('input', this.model); @@ -51,107 +73,115 @@ export default { </script> <template> <div> - <h2 class="gl-font-weight-normal gl-font-lg gl-my-5"> + <h2 class="gl-font-size-h2 gl-my-5"> {{ s__('Runners|Details') }} {{ __('(optional)') }} </h2> - <gl-form-group :label="s__('Runners|Runner description')" label-for="runner-description"> - <gl-form-input id="runner-description" v-model="model.description" name="description" /> - </gl-form-group> - <runner-maintenance-note-field v-model="model.maintenanceNote" class="gl-mt-5" /> + <gl-skeleton-loader v-if="loading" :lines="9" /> + <template v-else-if="model"> + <gl-form-group :label="s__('Runners|Runner description')" label-for="runner-description"> + <gl-form-input id="runner-description" v-model="model.description" name="description" /> + </gl-form-group> + <runner-maintenance-note-field v-model="model.maintenanceNote" class="gl-mt-5" /> + </template> <hr aria-hidden="true" /> - <h2 class="gl-font-weight-normal gl-font-lg gl-my-5"> + <h2 class="gl-font-size-h2 gl-my-5"> {{ s__('Runners|Configuration') }} {{ __('(optional)') }} </h2> - <div class="gl-mb-5"> - <gl-form-checkbox v-model="model.paused" name="paused"> - {{ __('Paused') }} - <template #help> - {{ s__('Runners|Stop the runner from accepting new jobs.') }} - </template> - </gl-form-checkbox> - - <gl-form-checkbox - v-model="model.accessLevel" - name="protected" - :value="$options.ACCESS_LEVEL_REF_PROTECTED" - :unchecked-value="$options.ACCESS_LEVEL_NOT_PROTECTED" - > - {{ __('Protected') }} - <template #help> - {{ s__('Runners|Use the runner on pipelines for protected branches only.') }} - </template> - </gl-form-checkbox> - - <gl-form-checkbox v-model="model.runUntagged" name="run-untagged"> - {{ __('Run untagged jobs') }} - <template #help> - {{ s__('Runners|Use the runner for jobs without tags in addition to tagged jobs.') }} - </template> - </gl-form-checkbox> - - <gl-form-checkbox v-if="canBeLockedToProject" v-model="model.locked" name="locked"> - {{ __('Lock to current projects') }} <gl-icon name="lock" /> - <template #help> - {{ - s__( - 'Runners|Use the runner for the currently assigned projects only. Only administrators can change the assigned projects.', - ) - }} - </template> - </gl-form-checkbox> - </div> + <gl-skeleton-loader v-if="loading" :lines="27" /> + <template v-else-if="model"> + <div class="gl-mb-5"> + <gl-form-checkbox v-model="model.paused" name="paused"> + {{ __('Paused') }} + <template #help> + {{ s__('Runners|Stop the runner from accepting new jobs.') }} + </template> + </gl-form-checkbox> - <gl-form-group :label="__('Tags')" label-for="runner-tags"> - <template #description> - <gl-sprintf - :message=" - s__('Runners|Multiple tags must be separated by a comma. For example, %{example}.') - " + <gl-form-checkbox + v-model="model.accessLevel" + name="protected" + :value="$options.ACCESS_LEVEL_REF_PROTECTED" + :unchecked-value="$options.ACCESS_LEVEL_NOT_PROTECTED" > - <template #example> - <!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings --> - <code>macos, shared</code> + {{ __('Protected') }} + <template #help> + {{ s__('Runners|Use the runner on pipelines for protected branches only.') }} </template> - </gl-sprintf> - </template> - <template #label-description> - <gl-sprintf - :message=" - s__( - 'Runners|Add tags for the types of jobs the runner processes to ensure that the runner only runs jobs that you intend it to. %{helpLinkStart}Learn more.%{helpLinkEnd}', - ) - " - > - <template #helpLink="{ content }"> - <gl-link :href="$options.HELP_LABELS_PAGE_PATH" target="_blank">{{ content }}</gl-link> + </gl-form-checkbox> + + <gl-form-checkbox v-model="model.runUntagged" name="run-untagged"> + {{ __('Run untagged jobs') }} + <template #help> + {{ s__('Runners|Use the runner for jobs without tags in addition to tagged jobs.') }} </template> - </gl-sprintf> - </template> - <gl-form-input id="runner-tags" v-model="model.tagList" name="tags" /> - </gl-form-group> + </gl-form-checkbox> - <gl-form-group - :label="__('Maximum job timeout')" - :label-description=" - s__( - 'Runners|Maximum amount of time the runner can run before it terminates. If a project has a shorter job timeout period, the job timeout period of the instance runner is used instead.', - ) - " - label-for="runner-max-timeout" - :description="s__('Runners|Enter the number of seconds.')" - > - <gl-form-input - id="runner-max-timeout" - v-model.number="model.maximumTimeout" - name="max-timeout" - type="number" - /> - </gl-form-group> + <gl-form-checkbox v-if="canBeLockedToProject" v-model="model.locked" name="locked"> + {{ __('Lock to current projects') }} <gl-icon name="lock" /> + <template #help> + {{ + s__( + 'Runners|Use the runner for the currently assigned projects only. Only administrators can change the assigned projects.', + ) + }} + </template> + </gl-form-checkbox> + </div> + + <gl-form-group :label="__('Tags')" label-for="runner-tags"> + <template #description> + <gl-sprintf + :message=" + s__('Runners|Multiple tags must be separated by a comma. For example, %{example}.') + " + > + <template #example> + <!-- eslint-disable-next-line @gitlab/vue-require-i18n-strings --> + <code>macos, shared</code> + </template> + </gl-sprintf> + </template> + <template #label-description> + <gl-sprintf + :message=" + s__( + 'Runners|Add tags for the types of jobs the runner processes to ensure that the runner only runs jobs that you intend it to. %{helpLinkStart}Learn more.%{helpLinkEnd}', + ) + " + > + <template #helpLink="{ content }"> + <gl-link :href="$options.HELP_LABELS_PAGE_PATH" target="_blank">{{ + content + }}</gl-link> + </template> + </gl-sprintf> + </template> + <gl-form-input id="runner-tags" v-model="model.tagList" name="tags" /> + </gl-form-group> + + <gl-form-group + :label="__('Maximum job timeout')" + :label-description=" + s__( + 'Runners|Maximum amount of time the runner can run before it terminates. If a project has a shorter job timeout period, the job timeout period of the instance runner is used instead.', + ) + " + label-for="runner-max-timeout" + :description="s__('Runners|Enter the number of seconds.')" + > + <gl-form-input + id="runner-max-timeout" + v-model.number="model.maximumTimeout" + name="max-timeout" + type="number" + /> + </gl-form-group> + </template> </div> </template> diff --git a/app/assets/javascripts/ci/runner/components/runner_update_form.vue b/app/assets/javascripts/ci/runner/components/runner_update_form.vue index aebddc70646..bc044b609a3 100644 --- a/app/assets/javascripts/ci/runner/components/runner_update_form.vue +++ b/app/assets/javascripts/ci/runner/components/runner_update_form.vue @@ -1,23 +1,16 @@ <script> -import { - GlButton, - GlIcon, - GlForm, - GlFormCheckbox, - GlFormGroup, - GlFormInputGroup, - GlSkeletonLoader, - GlTooltipDirective, -} from '@gitlab/ui'; -import { - modelToUpdateMutationVariables, - runnerToModel, -} from 'ee_else_ce/ci/runner/runner_update_form_utils'; +import { GlButton, GlForm } from '@gitlab/ui'; +import RunnerFormFields from '~/ci/runner/components/runner_form_fields.vue'; import { createAlert, VARIANT_SUCCESS } from '~/alert'; import { visitUrl } from '~/lib/utils/url_utility'; import { __ } from '~/locale'; import { captureException } from '~/ci/runner/sentry_utils'; -import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED, PROJECT_TYPE } from '../constants'; + +import { + modelToUpdateMutationVariables, + runnerToModel, +} from 'ee_else_ce/ci/runner/runner_update_form_utils'; +import { ACCESS_LEVEL_NOT_PROTECTED, ACCESS_LEVEL_REF_PROTECTED } from '../constants'; import runnerUpdateMutation from '../graphql/edit/runner_update.mutation.graphql'; import { saveAlertToLocalStorage } from '../local_storage_alert/save_alert_to_local_storage'; @@ -25,20 +18,11 @@ export default { name: 'RunnerUpdateForm', components: { GlButton, - GlIcon, GlForm, - GlFormCheckbox, - GlFormGroup, - GlFormInputGroup, - GlSkeletonLoader, - RunnerMaintenanceNoteField: () => - import('ee_component/ci/runner/components/runner_maintenance_note_field.vue'), + RunnerFormFields, RunnerUpdateCostFactorFields: () => import('ee_component/ci/runner/components/runner_update_cost_factor_fields.vue'), }, - directives: { - GlTooltip: GlTooltipDirective, - }, props: { runner: { type: Object, @@ -59,19 +43,12 @@ export default { data() { return { saving: false, - model: runnerToModel(this.runner), + model: null, }; }, - computed: { - canBeLockedToProject() { - return this.runner?.runnerType === PROJECT_TYPE; - }, - }, watch: { - runner(newVal, oldVal) { - if (oldVal === null) { - this.model = runnerToModel(newVal); - } + runner(val) { + this.model = runnerToModel(val); }, }, methods: { @@ -114,94 +91,8 @@ export default { </script> <template> <gl-form @submit.prevent="onSubmit"> - <h4 class="gl-font-lg gl-my-5">{{ s__('Runners|Details') }}</h4> - - <gl-skeleton-loader v-if="loading" /> - - <template v-else> - <gl-form-group :label="__('Description')" data-testid="runner-field-description"> - <gl-form-input-group v-model="model.description" /> - </gl-form-group> - <runner-maintenance-note-field v-model="model.maintenanceNote" /> - </template> - - <hr /> - - <h4 class="gl-font-lg gl-my-5">{{ s__('Runners|Configuration') }}</h4> - - <template v-if="loading"> - <gl-skeleton-loader v-for="i in 3" :key="i" /> - </template> - <template v-else> - <div class="gl-mb-5"> - <gl-form-checkbox v-model="model.paused" data-testid="runner-field-paused"> - {{ __('Paused') }} - <template #help> - {{ s__('Runners|Stop the runner from accepting new jobs.') }} - </template> - </gl-form-checkbox> - - <gl-form-checkbox - v-model="model.accessLevel" - data-testid="runner-field-protected" - :value="$options.ACCESS_LEVEL_REF_PROTECTED" - :unchecked-value="$options.ACCESS_LEVEL_NOT_PROTECTED" - > - {{ __('Protected') }} - <template #help> - {{ s__('Runners|Use the runner on pipelines for protected branches only.') }} - </template> - </gl-form-checkbox> - - <gl-form-checkbox v-model="model.runUntagged" data-testid="runner-field-run-untagged"> - {{ __('Run untagged jobs') }} - <template #help> - {{ s__('Runners|Use the runner for jobs without tags, in addition to tagged jobs.') }} - </template> - </gl-form-checkbox> - - <gl-form-checkbox - v-if="canBeLockedToProject" - v-model="model.locked" - data-testid="runner-field-locked" - > - {{ __('Lock to current projects') }} <gl-icon name="lock" /> - <template #help> - {{ - s__( - 'Runners|Use the runner for the currently assigned projects only. Only administrators can change the assigned projects.', - ) - }} - </template> - </gl-form-checkbox> - </div> - - <gl-form-group - data-testid="runner-field-max-timeout" - :label="__('Maximum job timeout')" - :description=" - s__( - 'Runners|Enter the number of seconds. This timeout takes precedence over lower timeouts set for the project.', - ) - " - > - <gl-form-input-group v-model.number="model.maximumTimeout" type="number" /> - </gl-form-group> - - <gl-form-group - data-testid="runner-field-tags" - :label="__('Tags')" - :description=" - __( - 'You can set up jobs to only use runners with specific tags. Separate tags with commas.', - ) - " - > - <gl-form-input-group v-model="model.tagList" /> - </gl-form-group> - - <runner-update-cost-factor-fields v-model="model" /> - </template> + <runner-form-fields v-model="model" :loading="loading" /> + <runner-update-cost-factor-fields v-model="model" /> <div class="gl-mt-6"> <gl-button diff --git a/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue b/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue index 5965330c4eb..2e1706ddae9 100644 --- a/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue +++ b/app/assets/javascripts/ci/runner/group_new_runner/group_new_runner_app.vue @@ -66,7 +66,7 @@ export default { <hr aria-hidden="true" /> - <h2 class="gl-font-weight-normal gl-font-lg gl-my-5"> + <h2 class="gl-font-size-h2 gl-my-5"> {{ s__('Runners|Platform') }} </h2> <runner-platforms-radio-group v-model="platform" /> diff --git a/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue b/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue index 715b0c28148..51f5a9ce8d9 100644 --- a/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue +++ b/app/assets/javascripts/ci/runner/project_new_runner/project_new_runner_app.vue @@ -66,7 +66,7 @@ export default { <hr aria-hidden="true" /> - <h2 class="gl-font-weight-normal gl-font-lg gl-my-5"> + <h2 class="gl-font-size-h2 gl-my-5"> {{ s__('Runners|Platform') }} </h2> <runner-platforms-radio-group v-model="platform" /> diff --git a/app/assets/javascripts/contextual_sidebar.js b/app/assets/javascripts/contextual_sidebar.js index ea444b5c146..ab5f01227fb 100644 --- a/app/assets/javascripts/contextual_sidebar.js +++ b/app/assets/javascripts/contextual_sidebar.js @@ -108,27 +108,5 @@ export default class ContextualSidebar { const collapse = parseBoolean(getCookie('sidebar_collapsed')); this.toggleCollapsedSidebar(collapse, true); } - - const modalEl = document.querySelector('.js-invite-members-modal'); - if (modalEl) { - import( - /* webpackChunkName: 'initInviteMembersModal' */ '~/invite_members/init_invite_members_modal' - ) - .then(({ default: initInviteMembersModal }) => { - initInviteMembersModal(); - }) - .catch(() => {}); - - const inviteTriggers = document.querySelectorAll('.js-invite-members-trigger'); - if (inviteTriggers) { - import( - /* webpackChunkName: 'initInviteMembersTrigger' */ '~/invite_members/init_invite_members_trigger' - ) - .then(({ default: initInviteMembersTrigger }) => { - initInviteMembersTrigger(); - }) - .catch(() => {}); - } - } } } diff --git a/app/assets/javascripts/environments/components/kubernetes_agent_info.vue b/app/assets/javascripts/environments/components/kubernetes_agent_info.vue index 7660912f93a..03bde8d64ac 100644 --- a/app/assets/javascripts/environments/components/kubernetes_agent_info.vue +++ b/app/assets/javascripts/environments/components/kubernetes_agent_info.vue @@ -1,68 +1,37 @@ <script> -import { GlIcon, GlLink, GlSprintf, GlLoadingIcon, GlAlert } from '@gitlab/ui'; +import { GlIcon, GlLink, GlSprintf } from '@gitlab/ui'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; +import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getAgentLastContact, getAgentStatus } from '~/clusters_list/clusters_util'; import { AGENT_STATUSES } from '~/clusters_list/constants'; import { s__ } from '~/locale'; -import getK8sClusterAgentQuery from '../graphql/queries/k8s_cluster_agent.query.graphql'; export default { components: { GlIcon, GlLink, GlSprintf, - GlLoadingIcon, TimeAgoTooltip, - GlAlert, }, props: { - agentName: { - required: true, - type: String, - }, - agentId: { - required: true, - type: String, - }, - agentProjectPath: { - required: true, - type: String, - }, - }, - apollo: { clusterAgent: { - query: getK8sClusterAgentQuery, - variables() { - return { - agentName: this.agentName, - projectPath: this.agentProjectPath, - }; - }, - update: (data) => data?.project?.clusterAgent, - error() { - this.clusterAgent = null; - }, + required: true, + type: Object, }, }, - data() { - return { - clusterAgent: null, - }; - }, computed: { - isLoading() { - return this.$apollo.queries.clusterAgent.loading; - }, agentLastContact() { return getAgentLastContact(this.clusterAgent.tokens.nodes); }, agentStatus() { return getAgentStatus(this.agentLastContact); }, + agentId() { + return getIdFromGraphQLId(this.clusterAgent.id); + }, }, methods: {}, i18n: { - loadingError: s__('ClusterAgents|An error occurred while loading your agent'), agentId: s__('ClusterAgents|Agent ID #%{agentId}'), neverConnectedText: s__('ClusterAgents|Never'), }, @@ -70,8 +39,7 @@ export default { }; </script> <template> - <gl-loading-icon v-if="isLoading" inline /> - <div v-else-if="clusterAgent" class="gl-text-gray-900"> + <div class="gl-text-gray-900"> <gl-icon name="kubernetes-agent" class="gl-text-gray-500" /> <gl-link :href="clusterAgent.webPath" class="gl-mr-3"> <gl-sprintf :message="$options.i18n.agentId" @@ -92,8 +60,4 @@ export default { <span v-else>{{ $options.i18n.neverConnectedText }}</span> </span> </div> - - <gl-alert v-else variant="danger" :dismissible="false"> - {{ $options.i18n.loadingError }} - </gl-alert> </template> diff --git a/app/assets/javascripts/environments/components/kubernetes_overview.vue b/app/assets/javascripts/environments/components/kubernetes_overview.vue index 1f15c4daa2f..a849adfc755 100644 --- a/app/assets/javascripts/environments/components/kubernetes_overview.vue +++ b/app/assets/javascripts/environments/components/kubernetes_overview.vue @@ -2,7 +2,7 @@ import { GlCollapse, GlButton, GlAlert } from '@gitlab/ui'; import { __, s__ } from '~/locale'; import csrf from '~/lib/utils/csrf'; -import { getIdFromGraphQLId, isGid } from '~/graphql_shared/utils'; +import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import KubernetesAgentInfo from './kubernetes_agent_info.vue'; import KubernetesPods from './kubernetes_pods.vue'; import KubernetesTabs from './kubernetes_tabs.vue'; @@ -18,17 +18,9 @@ export default { }, inject: ['kasTunnelUrl'], props: { - agentName: { + clusterAgent: { required: true, - type: String, - }, - agentId: { - required: true, - type: String, - }, - agentProjectPath: { - required: true, - type: String, + type: Object, }, namespace: { required: false, @@ -50,8 +42,7 @@ export default { return this.isVisible ? this.$options.i18n.collapse : this.$options.i18n.expand; }, gitlabAgentId() { - const id = isGid(this.agentId) ? getIdFromGraphQLId(this.agentId) : this.agentId; - return id.toString(); + return getIdFromGraphQLId(this.clusterAgent.id).toString(); }, k8sAccessConfiguration() { return { @@ -91,11 +82,7 @@ export default { </p> <gl-collapse :visible="isVisible" class="gl-md-pl-7 gl-md-pr-5 gl-mt-4"> <template v-if="isVisible"> - <kubernetes-agent-info - :agent-name="agentName" - :agent-id="agentId" - :agent-project-path="agentProjectPath" - class="gl-mb-5" /> + <kubernetes-agent-info :cluster-agent="clusterAgent" class="gl-mb-5" /> <gl-alert v-if="error" variant="danger" :dismissible="false" class="gl-mb-5"> {{ error }} diff --git a/app/assets/javascripts/environments/components/new_environment_item.vue b/app/assets/javascripts/environments/components/new_environment_item.vue index 9ad31688329..72323c0e43e 100644 --- a/app/assets/javascripts/environments/components/new_environment_item.vue +++ b/app/assets/javascripts/environments/components/new_environment_item.vue @@ -13,6 +13,7 @@ import { truncate } from '~/lib/utils/text_utility'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import isLastDeployment from '../graphql/queries/is_last_deployment.query.graphql'; +import getEnvironmentClusterAgent from '../graphql/queries/environment_cluster_agent.query.graphql'; import ExternalUrl from './environment_external_url.vue'; import Actions from './environment_actions.vue'; import StopComponent from './environment_stop.vue'; @@ -51,7 +52,7 @@ export default { GlTooltip, }, mixins: [glFeatureFlagsMixin()], - inject: ['helpPagePath'], + inject: ['helpPagePath', 'projectPath'], props: { environment: { required: true, @@ -81,7 +82,7 @@ export default { tierTooltip: s__('Environment|Deployment tier'), }, data() { - return { visible: false }; + return { visible: false, clusterAgent: null }; }, computed: { icon() { @@ -163,23 +164,33 @@ export default { rolloutStatus() { return this.environment?.rolloutStatus; }, - agent() { - return this.environment?.agent || {}; - }, isKubernetesOverviewAvailable() { return this.glFeatures?.kasUserAccessProject; }, - hasRequiredAgentData() { - const { project, id, name } = this.agent || {}; - return project && id && name; - }, showKubernetesOverview() { - return this.isKubernetesOverviewAvailable && this.hasRequiredAgentData; + return Boolean(this.isKubernetesOverviewAvailable && this.clusterAgent); }, }, methods: { - toggleCollapse() { + toggleEnvironmentCollapse() { this.visible = !this.visible; + + if (this.visible) { + this.getClusterAgent(); + } + }, + getClusterAgent() { + if (!this.isKubernetesOverviewAvailable || this.clusterAgent) return; + + this.$apollo.addSmartQuery('environmentClusterAgent', { + variables() { + return { environmentName: this.environment.name, projectFullPath: this.projectPath }; + }, + query: getEnvironmentClusterAgent, + update(data) { + this.clusterAgent = data?.project?.environment?.clusterAgent; + }, + }); }, }, deploymentClasses: [ @@ -222,7 +233,7 @@ export default { :aria-label="label" size="small" category="secondary" - @click="toggleCollapse" + @click="toggleEnvironmentCollapse" /> <gl-link v-gl-tooltip @@ -359,10 +370,8 @@ export default { </div> <div v-if="showKubernetesOverview" :class="$options.kubernetesOverviewClasses"> <kubernetes-overview - :agent-project-path="agent.project" - :agent-name="agent.name" - :agent-id="agent.id" - :namespace="agent.kubernetesNamespace" + :cluster-agent="clusterAgent" + :namespace="environment.kubernetesNamespace" /> </div> <div v-if="rolloutStatus" :class="$options.deployBoardClasses"> diff --git a/app/assets/javascripts/environments/graphql/queries/environment_cluster_agent.query.graphql b/app/assets/javascripts/environments/graphql/queries/environment_cluster_agent.query.graphql new file mode 100644 index 00000000000..760f1fba897 --- /dev/null +++ b/app/assets/javascripts/environments/graphql/queries/environment_cluster_agent.query.graphql @@ -0,0 +1,19 @@ +query getEnvironmentClusterAgent($projectFullPath: ID!, $environmentName: String) { + project(fullPath: $projectFullPath) { + id + environment(name: $environmentName) { + id + clusterAgent { + id + name + webPath + tokens { + nodes { + id + lastUsedAt + } + } + } + } + } +} diff --git a/app/assets/javascripts/environments/graphql/queries/k8s_cluster_agent.query.graphql b/app/assets/javascripts/environments/graphql/queries/k8s_cluster_agent.query.graphql deleted file mode 100644 index bd45d2dba2f..00000000000 --- a/app/assets/javascripts/environments/graphql/queries/k8s_cluster_agent.query.graphql +++ /dev/null @@ -1,15 +0,0 @@ -query getK8sClusterAgentQuery($projectPath: ID!, $agentName: String!) { - project(fullPath: $projectPath) { - id - clusterAgent(name: $agentName) { - id - webPath - tokens { - nodes { - id - lastUsedAt - } - } - } - } -} diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js index 63a1ba89fff..42682d9b79f 100644 --- a/app/assets/javascripts/layout_nav.js +++ b/app/assets/javascripts/layout_nav.js @@ -64,9 +64,31 @@ export function initScrollingTabs() { }); } -function initDeferred() { - initScrollingTabs(); +function initInviteMembers() { + const modalEl = document.querySelector('.js-invite-members-modal'); + if (!modalEl) return; + + import( + /* webpackChunkName: 'initInviteMembersModal' */ '~/invite_members/init_invite_members_modal' + ) + .then(({ default: initInviteMembersModal }) => { + initInviteMembersModal(); + }) + .catch(() => {}); + const inviteTriggers = document.querySelectorAll('.js-invite-members-trigger'); + if (!inviteTriggers) return; + + import( + /* webpackChunkName: 'initInviteMembersTrigger' */ '~/invite_members/init_invite_members_trigger' + ) + .then(({ default: initInviteMembersTrigger }) => { + initInviteMembersTrigger(); + }) + .catch(() => {}); +} + +function initWhatsNewComponent() { const appEl = document.getElementById('whats-new-app'); if (!appEl) return; @@ -84,6 +106,12 @@ function initDeferred() { }); } +function initDeferred() { + initScrollingTabs(); + initWhatsNewComponent(); + initInviteMembers(); +} + export default function initLayoutNav() { if (!gon.use_new_navigation) { const contextualSidebar = new ContextualSidebar(); diff --git a/app/assets/javascripts/pages/groups/group_members/index.js b/app/assets/javascripts/pages/groups/group_members/index.js index 2e71eced66f..df6ca8eab96 100644 --- a/app/assets/javascripts/pages/groups/group_members/index.js +++ b/app/assets/javascripts/pages/groups/group_members/index.js @@ -1,8 +1,6 @@ import { groupMemberRequestFormatter } from '~/groups/members/utils'; import initInviteGroupTrigger from '~/invite_members/init_invite_group_trigger'; import initInviteGroupsModal from '~/invite_members/init_invite_groups_modal'; -import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; -import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger'; import { s__ } from '~/locale'; import { initMembersApp } from '~/members'; import { MEMBER_TYPES, EE_APP_OPTIONS } from 'ee_else_ce/members/constants'; @@ -60,7 +58,5 @@ const APP_OPTIONS = { initMembersApp(document.querySelector('.js-group-members-list-app'), APP_OPTIONS); -initInviteMembersModal(); initInviteGroupsModal(); -initInviteMembersTrigger(); initInviteGroupTrigger(); diff --git a/app/assets/javascripts/pages/groups/shared/group_details.js b/app/assets/javascripts/pages/groups/shared/group_details.js index dba65c7e791..5d9eafe5672 100644 --- a/app/assets/javascripts/pages/groups/shared/group_details.js +++ b/app/assets/javascripts/pages/groups/shared/group_details.js @@ -1,6 +1,5 @@ import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import initInviteMembersBanner from '~/groups/init_invite_members_banner'; -import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; import initNotificationsDropdown from '~/notifications'; import ProjectsList from '~/projects_list'; @@ -12,5 +11,4 @@ export default function initGroupDetails() { new ProjectsList(); // eslint-disable-line no-new initInviteMembersBanner(); - initInviteMembersModal(); } diff --git a/app/assets/javascripts/pages/projects/project_members/index.js b/app/assets/javascripts/pages/projects/project_members/index.js index 79a4ed0f9c3..1e9111a3cc6 100644 --- a/app/assets/javascripts/pages/projects/project_members/index.js +++ b/app/assets/javascripts/pages/projects/project_members/index.js @@ -1,9 +1,7 @@ import initImportProjectMembersTrigger from '~/invite_members/init_import_project_members_trigger'; import initImportProjectMembersModal from '~/invite_members/init_import_project_members_modal'; import initInviteGroupTrigger from '~/invite_members/init_invite_group_trigger'; -import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; import initInviteGroupsModal from '~/invite_members/init_invite_groups_modal'; -import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger'; import { s__ } from '~/locale'; import { initMembersApp } from '~/members'; import { MEMBER_TYPES } from '~/members/constants'; @@ -11,9 +9,7 @@ import { groupLinkRequestFormatter } from '~/members/utils'; import { projectMemberRequestFormatter } from '~/projects/members/utils'; initImportProjectMembersModal(); -initInviteMembersModal(); initInviteGroupsModal(); -initInviteMembersTrigger(); initInviteGroupTrigger(); initImportProjectMembersTrigger(); diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js index 33d4090011f..e17f5255c54 100644 --- a/app/assets/javascripts/pages/projects/show/index.js +++ b/app/assets/javascripts/pages/projects/show/index.js @@ -1,7 +1,5 @@ import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; -import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; -import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger'; import initClustersDeprecationAlert from '~/projects/clusters_deprecation_alert'; import leaveByUrl from '~/namespaces/leave_by_url'; import initVueNotificationsDropdown from '~/notifications'; @@ -42,8 +40,6 @@ initVueNotificationsDropdown(); new ShortcutsNavigation(); // eslint-disable-line no-new initUploadFileTrigger(); -initInviteMembersModal(); -initInviteMembersTrigger(); initClustersDeprecationAlert(); initTerraformNotification(); diff --git a/app/assets/javascripts/pages/projects/work_items/index.js b/app/assets/javascripts/pages/projects/work_items/index.js index 6eef2352e2c..11c257611f0 100644 --- a/app/assets/javascripts/pages/projects/work_items/index.js +++ b/app/assets/javascripts/pages/projects/work_items/index.js @@ -1,5 +1,3 @@ import { initWorkItemsRoot } from '~/work_items/index'; -import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; initWorkItemsRoot(); -initInviteMembersModal(); diff --git a/app/assets/javascripts/sidebar/mount_sidebar.js b/app/assets/javascripts/sidebar/mount_sidebar.js index 74843bcc006..67e76b575e0 100644 --- a/app/assets/javascripts/sidebar/mount_sidebar.js +++ b/app/assets/javascripts/sidebar/mount_sidebar.js @@ -2,8 +2,6 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; import { TYPENAME_ISSUE, TYPENAME_MERGE_REQUEST } from '~/graphql_shared/constants'; import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils'; -import initInviteMembersModal from '~/invite_members/init_invite_members_modal'; -import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger'; import { TYPE_ISSUE, TYPE_MERGE_REQUEST, WORKSPACE_PROJECT } from '~/issues/constants'; import { gqlClient } from '~/issues/list/graphql'; import { @@ -805,8 +803,6 @@ const isAssigneesWidgetShown = (isInIssuePage() || isInDesignPage() || isInMRPage()) && gon.features.issueAssigneesWidget; export function mountSidebar(mediator, store) { - initInviteMembersModal(); - initInviteMembersTrigger(); mountSidebarTodoWidget(); if (isAssigneesWidgetShown) { mountSidebarAssigneesWidget(); diff --git a/app/assets/stylesheets/notify_enhanced.scss b/app/assets/stylesheets/notify_enhanced.scss index b331d997a97..a3e02dabe0e 100644 --- a/app/assets/stylesheets/notify_enhanced.scss +++ b/app/assets/stylesheets/notify_enhanced.scss @@ -32,6 +32,10 @@ body { font-size: inherit; } +pre { + font-size: 14px; +} + .gl-mb-5 { @include gl-mb-5; } diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb index 1c988b9767f..bf8dea15c58 100644 --- a/app/helpers/application_settings_helper.rb +++ b/app/helpers/application_settings_helper.rb @@ -99,11 +99,11 @@ module ApplicationSettingsHelper checked_value: level, unchecked_value: nil ) do |c| - c.label do + c.with_label do visibility_level_icon(level) + content_tag(:span, label, { class: 'gl-ml-2' }) end - c.help_text do + c.with_help_text do restricted_visibility_levels_help_text.fetch(level) end end diff --git a/app/helpers/form_helper.rb b/app/helpers/form_helper.rb index ed8cca20241..3d0b899e867 100644 --- a/app/helpers/form_helper.rb +++ b/app/helpers/form_helper.rb @@ -37,7 +37,7 @@ module FormHelper dismissible: false, alert_options: { id: 'error_explanation', class: 'gl-mb-5' } ) do |c| - c.body do + c.with_body do tag.ul(class: 'gl-pl-5 gl-mb-0') do messages end diff --git a/app/models/analytics/cycle_analytics/value_stream.rb b/app/models/analytics/cycle_analytics/value_stream.rb index 59c68393d74..31e06075bcb 100644 --- a/app/models/analytics/cycle_analytics/value_stream.rb +++ b/app/models/analytics/cycle_analytics/value_stream.rb @@ -21,6 +21,7 @@ module Analytics scope :preload_associated_models, -> { includes(:namespace, stages: [:namespace, :end_event_label, :start_event_label]) } + scope :order_by_name_asc, -> { order(arel_table[:name].lower.asc) } after_save :ensure_aggregation_record_presence diff --git a/app/models/packages/nuget/metadatum.rb b/app/models/packages/nuget/metadatum.rb index 1db8c0eddbf..08276f87568 100644 --- a/app/models/packages/nuget/metadatum.rb +++ b/app/models/packages/nuget/metadatum.rb @@ -1,24 +1,22 @@ # frozen_string_literal: true class Packages::Nuget::Metadatum < ApplicationRecord + MAX_AUTHORS_LENGTH = 255 + MAX_DESCRIPTION_LENGTH = 4000 + belongs_to :package, -> { where(package_type: :nuget) }, inverse_of: :nuget_metadatum validates :package, presence: true validates :license_url, public_url: { allow_blank: true } validates :project_url, public_url: { allow_blank: true } validates :icon_url, public_url: { allow_blank: true } + validates :authors, presence: true, length: { maximum: MAX_AUTHORS_LENGTH } + validates :description, presence: true, length: { maximum: MAX_DESCRIPTION_LENGTH } - validate :ensure_at_least_one_field_supplied validate :ensure_nuget_package_type private - def ensure_at_least_one_field_supplied - return if license_url? || project_url? || icon_url? - - errors.add(:base, _('Nuget metadatum must have at least license_url, project_url or icon_url set')) - end - def ensure_nuget_package_type return if package&.nuget? diff --git a/app/policies/project_policy.rb b/app/policies/project_policy.rb index 15f91cae86b..c70dc288710 100644 --- a/app/policies/project_policy.rb +++ b/app/policies/project_policy.rb @@ -163,6 +163,11 @@ class ProjectPolicy < BasePolicy condition(:service_desk_enabled) { @subject.service_desk_enabled? } with_scope :subject + condition(:model_experiments_enabled) do + Feature.enabled?(:ml_experiment_tracking, @subject) && @subject.feature_available?(:model_experiments, @user) + end + + with_scope :subject condition(:model_registry_enabled) { Feature.enabled?(:model_registry, @subject) } with_scope :subject @@ -223,6 +228,7 @@ class ProjectPolicy < BasePolicy feature_flags releases infrastructure + model_experiments ] features.each do |f| @@ -899,6 +905,10 @@ class ProjectPolicy < BasePolicy enable :read_model_registry end + rule { model_experiments_enabled }.policy do + enable :read_model_experiments + end + private def user_is_user? diff --git a/app/presenters/packages/nuget/presenter_helpers.rb b/app/presenters/packages/nuget/presenter_helpers.rb index 82ed80d8372..ea8558c54f4 100644 --- a/app/presenters/packages/nuget/presenter_helpers.rb +++ b/app/presenters/packages/nuget/presenter_helpers.rb @@ -5,7 +5,6 @@ module Packages module PresenterHelpers include ::API::Helpers::RelatedResourcesHelpers - BLANK_STRING = '' PACKAGE_DEPENDENCY_GROUP = 'PackageDependencyGroup' PACKAGE_DEPENDENCY = 'PackageDependency' @@ -45,14 +44,13 @@ module Packages def catalog_entry_for(package) { json_url: json_url_for(package), - authors: BLANK_STRING, dependency_groups: dependency_groups_for(package), package_name: package.name, package_version: package.version, archive_url: archive_url_for(package), - summary: BLANK_STRING, tags: tags_for(package), - metadatum: metadatum_for(package) + metadatum: metadatum_for(package), + published: package.created_at.iso8601 } end @@ -98,8 +96,8 @@ module Packages metadatum = package.nuget_metadatum return {} unless metadatum - metadatum.slice(:project_url, :license_url, :icon_url) - .compact + metadatum.slice(:authors, :description, :project_url, :license_url, :icon_url) + .compact end def base_path_for(package) diff --git a/app/presenters/packages/nuget/search_results_presenter.rb b/app/presenters/packages/nuget/search_results_presenter.rb index 311296d576c..45c2c5170ae 100644 --- a/app/presenters/packages/nuget/search_results_presenter.rb +++ b/app/presenters/packages/nuget/search_results_presenter.rb @@ -20,11 +20,9 @@ module Packages { type: 'Package', - authors: '', name: package_name, version: latest_version, versions: build_package_versions(packages), - summary: '', total_downloads: 0, verified: true, tags: tags_for(latest_package), @@ -48,7 +46,7 @@ module Packages def latest_version(packages) versions = packages.map(&:version).compact - VersionSorter.sort(versions).last # rubocop: disable Style/RedundantSort + VersionSorter.sort(versions).last end end end diff --git a/app/services/ci/runners/assign_runner_service.rb b/app/services/ci/runners/assign_runner_service.rb index 290f945cc72..4e7b08bdd7a 100644 --- a/app/services/ci/runners/assign_runner_service.rb +++ b/app/services/ci/runners/assign_runner_service.rb @@ -17,6 +17,10 @@ module Ci return ServiceResponse.error(message: 'user not allowed to assign runner', http_status: :forbidden) end + unless @user.can?(:register_project_runners, @project) + return ServiceResponse.error(message: 'user not allowed to add runners to project', http_status: :forbidden) + end + if @runner.assign_to(@project, @user) ServiceResponse.success else diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index efe42fb29d5..f982d66eb08 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -124,6 +124,10 @@ module Issues def update_project_counter_caches?(issue) super || issue.confidential_changed? end + + def log_audit_event(issue, user, event_type, message) + # defined in EE + end end end diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb index e45033f2b91..f848a8db12a 100644 --- a/app/services/issues/close_service.rb +++ b/app/services/issues/close_service.rb @@ -28,6 +28,11 @@ module Issues event_service.close_issue(issue, current_user) create_note(issue, closed_via) if system_note + if current_user.project_bot? + log_audit_event(issue, current_user, "#{issue.issue_type}_closed_by_project_bot", + "Closed #{issue.issue_type.humanize(capitalize: false)} #{issue.title}") + end + closed_via = _("commit %{commit_id}") % { commit_id: closed_via.id } if closed_via.is_a?(Commit) notification_service.async.close_issue(issue, current_user, { closed_via: closed_via }) if notifications diff --git a/app/services/issues/reopen_service.rb b/app/services/issues/reopen_service.rb index f4d229ecec7..d71ba4e3414 100644 --- a/app/services/issues/reopen_service.rb +++ b/app/services/issues/reopen_service.rb @@ -7,6 +7,12 @@ module Issues if issue.reopen event_service.reopen_issue(issue, current_user) + + if current_user.project_bot? + log_audit_event(issue, current_user, "#{issue.issue_type}_reopened_by_project_bot", + "Reopened #{issue.issue_type.humanize(capitalize: false)} #{issue.title}") + end + create_note(issue, 'reopened') notification_service.async.reopen_issue(issue, current_user) perform_incident_management_actions(issue) diff --git a/app/services/packages/nuget/metadata_extraction_service.rb b/app/services/packages/nuget/metadata_extraction_service.rb index 02086b2a282..3b92d977c79 100644 --- a/app/services/packages/nuget/metadata_extraction_service.rb +++ b/app/services/packages/nuget/metadata_extraction_service.rb @@ -7,18 +7,22 @@ module Packages ExtractionError = Class.new(StandardError) + ROOT_XPATH = '//xmlns:package/xmlns:metadata/xmlns' + XPATHS = { - package_name: '//xmlns:package/xmlns:metadata/xmlns:id', - package_version: '//xmlns:package/xmlns:metadata/xmlns:version', - license_url: '//xmlns:package/xmlns:metadata/xmlns:licenseUrl', - project_url: '//xmlns:package/xmlns:metadata/xmlns:projectUrl', - icon_url: '//xmlns:package/xmlns:metadata/xmlns:iconUrl' + package_name: "#{ROOT_XPATH}:id", + package_version: "#{ROOT_XPATH}:version", + authors: "#{ROOT_XPATH}:authors", + description: "#{ROOT_XPATH}:description", + license_url: "#{ROOT_XPATH}:licenseUrl", + project_url: "#{ROOT_XPATH}:projectUrl", + icon_url: "#{ROOT_XPATH}:iconUrl" }.freeze - XPATH_DEPENDENCIES = '//xmlns:package/xmlns:metadata/xmlns:dependencies/xmlns:dependency' - XPATH_DEPENDENCY_GROUPS = '//xmlns:package/xmlns:metadata/xmlns:dependencies/xmlns:group' - XPATH_TAGS = '//xmlns:package/xmlns:metadata/xmlns:tags' - XPATH_PACKAGE_TYPES = '//xmlns:package/xmlns:metadata/xmlns:packageTypes/xmlns:packageType' + XPATH_DEPENDENCIES = "#{ROOT_XPATH}:dependencies/xmlns:dependency".freeze + XPATH_DEPENDENCY_GROUPS = "#{ROOT_XPATH}:dependencies/xmlns:group".freeze + XPATH_TAGS = "#{ROOT_XPATH}:tags".freeze + XPATH_PACKAGE_TYPES = "#{ROOT_XPATH}:packageTypes/xmlns:packageType".freeze MAX_FILE_SIZE = 4.megabytes.freeze @@ -40,10 +44,6 @@ module Packages end end - def project - package_file.package.project - end - def valid_package_file? package_file && package_file.package&.nuget? && diff --git a/app/services/packages/nuget/sync_metadatum_service.rb b/app/services/packages/nuget/sync_metadatum_service.rb index ca9cc4d5b78..189b972c156 100644 --- a/app/services/packages/nuget/sync_metadatum_service.rb +++ b/app/services/packages/nuget/sync_metadatum_service.rb @@ -15,6 +15,8 @@ module Packages metadatum.destroy! if metadatum.persisted? else metadatum.update!( + authors: authors, + description: description, license_url: license_url, project_url: project_url, icon_url: icon_url @@ -24,26 +26,57 @@ module Packages private + attr_reader :package, :metadata + def metadatum - strong_memoize(:metadatum) do - @package.nuget_metadatum || @package.build_nuget_metadatum - end + package.nuget_metadatum || package.build_nuget_metadatum end + strong_memoize_attr :metadatum def blank_metadata? - project_url.blank? && license_url.blank? && icon_url.blank? + [authors, description, project_url, license_url, icon_url].all?(&:blank?) + end + + def authors + truncate_value(:authors, ::Packages::Nuget::Metadatum::MAX_AUTHORS_LENGTH) end + strong_memoize_attr :authors + + def description + truncate_value(:description, ::Packages::Nuget::Metadatum::MAX_DESCRIPTION_LENGTH) + end + strong_memoize_attr :description def project_url - @metadata[:project_url] + metadata[:project_url] end def license_url - @metadata[:license_url] + metadata[:license_url] end def icon_url - @metadata[:icon_url] + metadata[:icon_url] + end + + def truncate_value(field, max_length) + return unless metadata[field] + + if metadata[field].size > max_length + log_info("#{field.capitalize} is too long (maximum is #{max_length} characters)", field) + end + + metadata[field].truncate(max_length) + end + + def log_info(message, field) + Gitlab::AppLogger.info( + class: self.class.name, + message: message, + package_id: package.id, + project_id: package.project_id, + field => metadata[field] + ) end end end diff --git a/app/services/packages/nuget/update_package_from_metadata_service.rb b/app/services/packages/nuget/update_package_from_metadata_service.rb index 5456ad4cad7..7153a9035b8 100644 --- a/app/services/packages/nuget/update_package_from_metadata_service.rb +++ b/app/services/packages/nuget/update_package_from_metadata_service.rb @@ -17,7 +17,7 @@ module Packages end def execute - raise InvalidMetadataError, 'package name and/or package version not found in metadata' unless valid_metadata? + raise InvalidMetadataError, 'package name, version, authors and/or description not found in metadata' unless valid_metadata? try_obtain_lease do @package_file.transaction do @@ -55,17 +55,19 @@ module Packages return if symbol_package? ::Packages::Nuget::SyncMetadatumService - .new(package, metadata.slice(:project_url, :license_url, :icon_url)) + .new(package, metadata.slice(:authors, :description, :project_url, :license_url, :icon_url)) .execute + ::Packages::UpdateTagsService .new(package, package_tags) .execute + rescue StandardError => e raise InvalidMetadataError, e.message end def valid_metadata? - package_name.present? && package_version.present? + [package_name, package_version, package_authors, package_description].all?(&:present?) end def link_to_existing_package @@ -93,15 +95,14 @@ module Packages end def existing_package - strong_memoize(:existing_package) do - @package_file.project.packages - .nuget - .with_name(package_name) - .with_version(package_version) - .not_pending_destruction - .first - end + @package_file.project.packages + .nuget + .with_name(package_name) + .with_version(package_version) + .not_pending_destruction + .first end + strong_memoize_attr :existing_package def package_name metadata[:package_name] @@ -123,15 +124,22 @@ module Packages metadata.fetch(:package_types, []) end + def package_authors + metadata[:authors] + end + + def package_description + metadata[:description] + end + def symbol_package? package_types.include?(SYMBOL_PACKAGE_IDENTIFIER) end def metadata - strong_memoize(:metadata) do - ::Packages::Nuget::MetadataExtractionService.new(@package_file.id).execute - end + ::Packages::Nuget::MetadataExtractionService.new(@package_file.id).execute end + strong_memoize_attr :metadata def package_filename "#{package_name.downcase}.#{package_version.downcase}.#{symbol_package? ? 'snupkg' : 'nupkg'}" diff --git a/app/views/projects/mirrors/_branch_filter.html.haml b/app/views/projects/mirrors/_branch_filter.html.haml index 49b0f8c39c8..7d90906bfe8 100644 --- a/app/views/projects/mirrors/_branch_filter.html.haml +++ b/app/views/projects/mirrors/_branch_filter.html.haml @@ -2,8 +2,8 @@ = render Pajamas::CheckboxTagComponent.new(name: :only_protected_branches, checkbox_options: { class: 'js-mirror-protected' }, label_options: { class: 'gl-mb-0!' }) do |c| - = c.label do + - c.with_label do = _('Mirror only protected branches') - = c.help_text do + - c.with_help_text do = _('If enabled, only protected branches will be mirrored.') = link_to _('Learn more.'), help_page_path('user/project/repository/mirror/index.md', anchor: 'mirror-only-protected-branches'), target: '_blank', rel: 'noopener noreferrer' diff --git a/app/views/projects/mirrors/_mirror_repos_push.html.haml b/app/views/projects/mirrors/_mirror_repos_push.html.haml index 0947359a753..5b02d650989 100644 --- a/app/views/projects/mirrors/_mirror_repos_push.html.haml +++ b/app/views/projects/mirrors/_mirror_repos_push.html.haml @@ -12,8 +12,8 @@ = render Pajamas::CheckboxTagComponent.new(name: :keep_divergent_refs, checkbox_options: { class: 'js-mirror-keep-divergent-refs' }, label_options: { class: 'gl-mb-0!' }) do |c| - = c.label do + - c.with_label do = _('Keep divergent refs') - = c.help_text do + - c.with_help_text do - link_opening_tag = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe = html_escape(_('Do not force push over diverged refs. After the mirror is created, this setting can only be modified using the API. %{mirroring_docs_link_start}Learn more about this option%{link_closing_tag} and %{mirroring_api_docs_link_start}the API.%{link_closing_tag}')) % { mirroring_docs_link_start: link_opening_tag % {url: help_page_path('user/project/repository/mirror/push.md', anchor: 'keep-divergent-refs')}, mirroring_api_docs_link_start: link_opening_tag % {url: help_page_path('api/remote_mirrors')}, link_closing_tag: '</a>'.html_safe } diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index b31e8919832..c4630eec168 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -10,7 +10,7 @@ = render Pajamas::ButtonComponent.new(type: :submit, variant: :confirm, icon: 'search') .form-group{ class: 'gl-ml-5 gl-mb-n3!' } = render Pajamas::CheckboxTagComponent.new(name: :filter_ref, checked: @options[:filter_ref]) do |c| - = c.label do + - c.with_label do = _("Begin with the selected commit") - if @commit diff --git a/app/views/shared/_new_merge_request_checkbox.html.haml b/app/views/shared/_new_merge_request_checkbox.html.haml index 75289e2e6a5..fb3dfba2691 100644 --- a/app/views/shared/_new_merge_request_checkbox.html.haml +++ b/app/views/shared/_new_merge_request_checkbox.html.haml @@ -3,7 +3,7 @@ = render Pajamas::CheckboxTagComponent.new(name: 'create_merge_request', checked: true, checkbox_options: { class: 'js-create-merge-request', id: "create_merge_request-#{nonce}" }) do |c| - = c.label do + - c.with_label do - translation_variables = { new_merge_request: "<strong>#{_('new merge request')}</strong>" } - translation = _('Start a %{new_merge_request} with these changes') % translation_variables #{ translation.html_safe } diff --git a/app/views/shared/issuable/form/_metadata.html.haml b/app/views/shared/issuable/form/_metadata.html.haml index 1dc24d205d0..1da0b82b634 100644 --- a/app/views/shared/issuable/form/_metadata.html.haml +++ b/app/views/shared/issuable/form/_metadata.html.haml @@ -7,10 +7,10 @@ - if @add_related_issue .form-group = render Pajamas::CheckboxTagComponent.new(name: :add_related_issue, value: @add_related_issue.iid, checked: true) do |c| - = c.label do + - c.with_label do - add_related_issue_link = link_to "\##{@add_related_issue.iid}", issue_path(@add_related_issue), class: ['has-tooltip'], title: @add_related_issue.title #{_('Relate to %{issuable_type} %{add_related_issue_link}').html_safe % { issuable_type: @add_related_issue.issue_type, add_related_issue_link: add_related_issue_link }} - = c.help_text do + - c.with_help_text do = _('Adds this %{issuable_type} as related to the %{issuable_type} it was created from') % { issuable_type: @add_related_issue.issue_type } - if issuable.respond_to?(:confidential) && can?(current_user, :set_confidentiality, issuable) diff --git a/app/workers/new_issue_worker.rb b/app/workers/new_issue_worker.rb index 07699a50e36..0e7f11debd2 100644 --- a/app/workers/new_issue_worker.rb +++ b/app/workers/new_issue_worker.rb @@ -28,5 +28,15 @@ class NewIssueWorker # rubocop:disable Scalability/IdempotentWorker Issues::AfterCreateService .new(container: issuable.project, current_user: user) .execute(issuable) + + log_audit_event if user.project_bot? + end + + private + + def log_audit_event + # defined in EE end end + +NewIssueWorker.prepend_mod |