diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-09 12:10:54 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-09 12:10:54 +0300 |
commit | c2de38f36d2fb75a17ce161fa69f2b8a5e670f3e (patch) | |
tree | d24a576c60f21055b8e2bc7f0d9954830d8a229c /app | |
parent | 40338034578aca9d622651a060cbf157a941361b (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
14 files changed, 238 insertions, 166 deletions
diff --git a/app/assets/javascripts/comment_templates/components/form.vue b/app/assets/javascripts/comment_templates/components/form.vue index 334c67ca339..0dadec7a5c5 100644 --- a/app/assets/javascripts/comment_templates/components/form.vue +++ b/app/assets/javascripts/comment_templates/components/form.vue @@ -5,6 +5,7 @@ import MarkdownField from '~/vue_shared/components/markdown/field.vue'; import { helpPagePath } from '~/helpers/help_page_helper'; import { logError } from '~/lib/logger'; import { __ } from '~/locale'; +import { InternalEvents } from '~/tracking'; import createSavedReplyMutation from '../queries/create_saved_reply.mutation.graphql'; import updateSavedReplyMutation from '../queries/update_saved_reply.mutation.graphql'; @@ -17,6 +18,7 @@ export default { GlAlert, MarkdownField, }, + mixins: [InternalEvents.mixin()], props: { id: { type: String, @@ -91,6 +93,7 @@ export default { this.$emit('saved'); this.updateCommentTemplate = { name: '', content: '' }; this.showValidation = false; + this.track_event('i_code_review_saved_replies_create'); } }, }) diff --git a/app/assets/javascripts/deploy_freeze/components/deploy_freeze_modal.vue b/app/assets/javascripts/deploy_freeze/components/deploy_freeze_modal.vue index b13b0ede9f0..e1c47fde81d 100644 --- a/app/assets/javascripts/deploy_freeze/components/deploy_freeze_modal.vue +++ b/app/assets/javascripts/deploy_freeze/components/deploy_freeze_modal.vue @@ -24,10 +24,10 @@ export default { static: true, lazy: true, }, - translations: { + i18n: { cronPlaceholder: '* * * * *', cronSyntaxInstructions: __( - 'Define a custom deploy freeze pattern with %{cronSyntaxStart}cron syntax%{cronSyntaxEnd}', + 'Define a custom deploy freeze pattern with %{cronSyntaxStart}cron syntax%{cronSyntaxEnd}.', ), addTitle: __('Add deploy freeze'), editTitle: __('Edit deploy freeze'), @@ -81,9 +81,7 @@ export default { return Boolean(this.selectedId); }, modalTitle() { - return this.isEditing - ? this.$options.translations.editTitle - : this.$options.translations.addTitle; + return this.isEditing ? this.$options.i18n.editTitle : this.$options.i18n.addTitle; }, }, methods: { @@ -104,6 +102,13 @@ export default { this.addFreezePeriod(); } }, + focusFirstInput() { + if (this.$refs.freezeStartCron) { + setTimeout(() => { + this.$refs.freezeStartCron?.$el?.focus(); + }, 250); + } + }, }, }; </script> @@ -115,9 +120,10 @@ export default { :action-primary="addDeployFreezeButton" @primary="submit" @canceled="resetModalHandler" + @change="focusFirstInput" > <p> - <gl-sprintf :message="$options.translations.cronSyntaxInstructions"> + <gl-sprintf :message="$options.i18n.cronSyntaxInstructions"> <template #cronSyntax="{ content }"> <gl-link href="https://crontab.guru/" target="_blank">{{ content }}</gl-link> </template> @@ -132,11 +138,13 @@ export default { > <gl-form-input id="deploy-freeze-start" + ref="freezeStartCron" v-model="freezeStartCron" class="gl-font-monospace!" data-qa-selector="deploy_freeze_start_field" - :placeholder="$options.translations.cronPlaceholder" + :placeholder="$options.i18n.cronPlaceholder" :state="freezeStartCronState" + autofocus trim /> </gl-form-group> @@ -152,7 +160,7 @@ export default { v-model="freezeEndCron" class="gl-font-monospace!" data-qa-selector="deploy_freeze_end_field" - :placeholder="$options.translations.cronPlaceholder" + :placeholder="$options.i18n.cronPlaceholder" :state="freezeEndCronState" trim /> diff --git a/app/assets/javascripts/deploy_freeze/components/deploy_freeze_table.vue b/app/assets/javascripts/deploy_freeze/components/deploy_freeze_table.vue index 77767456f76..24ea8f01c20 100644 --- a/app/assets/javascripts/deploy_freeze/components/deploy_freeze_table.vue +++ b/app/assets/javascripts/deploy_freeze/components/deploy_freeze_table.vue @@ -1,39 +1,48 @@ <script> -import { GlTable, GlButton, GlModal, GlModalDirective, GlSprintf } from '@gitlab/ui'; +import { + GlCard, + GlTable, + GlButton, + GlIcon, + GlModal, + GlModalDirective, + GlSprintf, +} from '@gitlab/ui'; import { mapState, mapActions } from 'vuex'; -import { s__ } from '~/locale'; +import { __, s__ } from '~/locale'; export default { fields: [ { key: 'freezeStart', label: s__('DeployFreeze|Freeze start'), + tdClass: 'gl-vertical-align-middle!', }, { key: 'freezeEnd', label: s__('DeployFreeze|Freeze end'), + tdClass: 'gl-vertical-align-middle!', }, { key: 'cronTimezone', label: s__('DeployFreeze|Time zone'), + tdClass: 'gl-vertical-align-middle!', }, { - key: 'edit', - label: s__('DeployFreeze|Edit'), - }, - { - key: 'delete', - label: s__('DeployFreeze|Delete'), + key: 'actions', + label: __('Actions'), + thClass: 'gl-text-right', }, ], - translations: { + i18n: { + title: s__('DeployFreeze|Deploy freezes'), addDeployFreeze: s__('DeployFreeze|Add deploy freeze'), deleteDeployFreezeTitle: s__('DeployFreeze|Delete deploy freeze?'), deleteDeployFreezeMessage: s__( 'DeployFreeze|Deploy freeze from %{start} to %{end} in %{timezone} will be removed. Are you sure?', ), emptyStateText: s__( - 'DeployFreeze|No deploy freezes exist for this project. To add one, select %{strongStart}Add deploy freeze%{strongEnd}', + 'DeployFreeze|No deploy freezes exist for this project. To add one, select %{strongStart}Add deploy freeze%{strongEnd} above.', ), }, modal: { @@ -42,10 +51,16 @@ export default { text: s__('DeployFreeze|Delete freeze period'), attributes: { variant: 'danger', 'data-testid': 'modal-confirm' }, }, + actionSecondary: { + text: __('Cancel'), + attributes: { variant: 'default' }, + }, }, components: { + GlCard, GlTable, GlButton, + GlIcon, GlModal, GlSprintf, }, @@ -80,65 +95,78 @@ export default { </script> <template> - <div class="deploy-freeze-table"> + <gl-card + class="gl-new-card deploy-freeze-table" + header-class="gl-new-card-header" + body-class="gl-new-card-body gl-px-0" + > + <template #header> + <div class="gl-new-card-title-wrapper"> + <h3 class="gl-new-card-title">{{ $options.i18n.title }}</h3> + <span class="gl-new-card-count"> + <gl-icon name="deployments" class="gl-mr-2" /> + {{ freezePeriods.length }} + </span> + </div> + <div class="gl-new-card-actions"> + <gl-button v-gl-modal.deploy-freeze-modal size="small" data-testid="add-deploy-freeze">{{ + $options.i18n.addDeployFreeze + }}</gl-button> + </div> + </template> + <gl-table data-testid="deploy-freeze-table" :items="freezePeriods" :fields="$options.fields" show-empty - stacked="lg" + stacked="md" > <template #cell(cronTimezone)="{ item }"> {{ item.cronTimezone.formattedTimezone }} </template> - <template #cell(edit)="{ item }"> - <gl-button - v-gl-modal.deploy-freeze-modal - icon="pencil" - data-testid="edit-deploy-freeze" - :aria-label="__('Edit deploy freeze')" - @click="setFreezePeriod(item)" - /> - </template> - <template #cell(delete)="{ item }"> - <gl-button - v-gl-modal="$options.modal.id" - category="secondary" - variant="danger" - icon="remove" - :aria-label="$options.modal.actionPrimary.text" - :loading="item.isDeleting" - data-testid="delete-deploy-freeze" - @click="handleDeleteFreezePeriod(item)" - /> + <template #cell(actions)="{ item }"> + <div class="gl-display-flex gl-justify-content-end gl-mt-n2 gl-mb-n2"> + <gl-button + v-gl-modal.deploy-freeze-modal + icon="pencil" + data-testid="edit-deploy-freeze" + :aria-label="__('Edit deploy freeze')" + class="gl-mr-3" + @click="setFreezePeriod(item)" + /> + <gl-button + v-gl-modal="$options.modal.id" + category="secondary" + variant="danger" + icon="remove" + :aria-label="$options.modal.actionPrimary.text" + :loading="item.isDeleting" + data-testid="delete-deploy-freeze" + @click="handleDeleteFreezePeriod(item)" + /> + </div> </template> <template #empty> - <p data-testid="empty-freeze-periods" class="gl-text-center text-plain"> - <gl-sprintf :message="$options.translations.emptyStateText"> + <p data-testid="empty-freeze-periods" class="gl-text-secondary gl-text-center gl-mb-0"> + <gl-sprintf :message="$options.i18n.emptyStateText"> <template #strong="{ content }"> - <strong>{{ content }}</strong> + {{ content }} </template> </gl-sprintf> </p> </template> </gl-table> - <gl-button - v-gl-modal.deploy-freeze-modal - data-testid="add-deploy-freeze" - category="primary" - variant="confirm" - > - {{ $options.translations.addDeployFreeze }} - </gl-button> <gl-modal - :title="$options.translations.deleteDeployFreezeTitle" + :title="$options.i18n.deleteDeployFreezeTitle" :modal-id="$options.modal.id" :action-primary="$options.modal.actionPrimary" + :action-secondary="$options.modal.actionSecondary" static @primary="confirmDeleteFreezePeriod" > <template v-if="freezePeriodToDelete"> - <gl-sprintf :message="$options.translations.deleteDeployFreezeMessage"> + <gl-sprintf :message="$options.i18n.deleteDeployFreezeMessage"> <template #start> <code>{{ freezePeriodToDelete.freezeStart }}</code> </template> @@ -149,5 +177,5 @@ export default { </gl-sprintf> </template> </gl-modal> - </div> + </gl-card> </template> diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue index bb2ca838279..b58e2249829 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/components/details_page/tags_list.vue @@ -6,6 +6,7 @@ import { fetchPolicies } from '~/lib/graphql'; import { joinPaths } from '~/lib/utils/url_utility'; import Tracking from '~/tracking'; import RegistryList from '~/packages_and_registries/shared/components/registry_list.vue'; +import PersistedPagination from '~/packages_and_registries/shared/components/persisted_pagination.vue'; import PersistedSearch from '~/packages_and_registries/shared/components/persisted_search.vue'; import TagsLoader from '~/packages_and_registries/shared/components/tags_loader.vue'; import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants'; @@ -27,6 +28,7 @@ import { import getContainerRepositoryTagsQuery from '../../graphql/queries/get_container_repository_tags.query.graphql'; import deleteContainerRepositoryTagsMutation from '../../graphql/mutations/delete_container_repository_tags.mutation.graphql'; import DeleteModal from '../delete_modal.vue'; +import { getPageParams, getNextPageParams, getPreviousPageParams } from '../../utils'; import TagsListRow from './tags_list_row.vue'; export default { @@ -37,6 +39,7 @@ export default { TagsListRow, TagsLoader, RegistryList, + PersistedPagination, PersistedSearch, }, mixins: [Tracking.mixin()], @@ -62,7 +65,7 @@ export default { required: false, }, }, - searchConfig: { NAME_SORT_FIELD }, + sortableFields: [NAME_SORT_FIELD], i18n: { REMOVE_TAGS_BUTTON_TITLE, TAGS_LIST_TITLE, @@ -87,7 +90,7 @@ export default { containerRepository: {}, filters: {}, itemsToBeDeleted: [], - mutationLoading: false, + isDeleteInProgress: false, sort: null, pageParams: {}, }; @@ -121,7 +124,7 @@ export default { return ( this.isImageLoading || this.$apollo.queries.containerRepository.loading || - this.mutationLoading || + this.isDeleteInProgress || !this.sort ); }, @@ -153,7 +156,7 @@ export default { async handleDeleteTag() { this.track('confirm_delete'); const { itemsToBeDeleted } = this; - this.mutationLoading = true; + this.isDeleteInProgress = true; try { const { data } = await this.$apollo.mutate({ mutation: deleteContainerRepositoryTagsMutation, @@ -180,24 +183,17 @@ export default { } catch (e) { this.$emit('delete', itemsToBeDeleted.length === 1 ? ALERT_DANGER_TAG : ALERT_DANGER_TAGS); } finally { - this.mutationLoading = false; + this.isDeleteInProgress = false; } }, fetchNextPage() { - this.pageParams = { - after: this.tagsPageInfo?.endCursor, - first: GRAPHQL_PAGE_SIZE, - }; + this.pageParams = getNextPageParams(this.tagsPageInfo?.endCursor); }, fetchPreviousPage() { - this.pageParams = { - first: null, - before: this.tagsPageInfo?.startCursor, - last: GRAPHQL_PAGE_SIZE, - }; + this.pageParams = getPreviousPageParams(this.tagsPageInfo?.startCursor); }, - handleSearchUpdate({ sort, filters }) { - this.pageParams = {}; + handleSearchUpdate({ sort, filters, pageInfo }) { + this.pageParams = getPageParams(pageInfo); this.sort = sort; const parsed = { @@ -224,10 +220,8 @@ export default { <div> <persisted-search class="gl-mb-5" - :sortable-fields="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ [ - $options.searchConfig.NAME_SORT_FIELD, - ] /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */" - :default-order="$options.searchConfig.NAME_SORT_FIELD.orderBy" + :sortable-fields="$options.sortableFields" + :default-order="$options.sortableFields[0].orderBy" default-sort="asc" @update="handleSearchUpdate" /> @@ -244,11 +238,8 @@ export default { <registry-list :hidden-delete="hideBulkDelete" :title="listTitle" - :pagination="tagsPageInfo" :items="tags" id-property="name" - @prev-page="fetchPreviousPage" - @next-page="fetchNextPage" @delete="deleteTags" > <template #default="{ selectItem, isSelected, item, first }"> @@ -272,5 +263,14 @@ export default { /> </template> </template> + + <div v-if="!isDeleteInProgress" class="gl-display-flex gl-justify-content-center"> + <persisted-pagination + class="gl-mt-3" + :pagination="tagsPageInfo" + @prev="fetchPreviousPage" + @next="fetchNextPage" + /> + </div> </div> </template> diff --git a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue index d44bdd1bba2..df87ee79111 100644 --- a/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue +++ b/app/assets/javascripts/packages_and_registries/container_registry/explorer/pages/list.vue @@ -339,7 +339,7 @@ export default { </template> </template> - <div class="gl-display-flex gl-justify-content-center"> + <div v-if="!mutationLoading" class="gl-display-flex gl-justify-content-center"> <persisted-pagination class="gl-mt-3" :pagination="pageInfo" diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 4cc1ed092d2..55a60e21784 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -110,10 +110,13 @@ class Projects::EnvironmentsController < Projects::ApplicationController return render_404 unless @environment.available? stop_actions = @environment.stop_with_actions!(current_user) + job = stop_actions.first if stop_actions&.count == 1 action_or_env_url = - if stop_actions&.count == 1 - polymorphic_url([project, stop_actions.first]) + if job.instance_of?(::Ci::Build) + polymorphic_url([project, job]) + elsif job.instance_of?(::Ci::Bridge) + project_pipeline_url(project, job.pipeline_id) else project_environment_url(project, @environment) end diff --git a/app/helpers/mirror_helper.rb b/app/helpers/mirror_helper.rb index 06deaeb5e9e..158aa5e0944 100644 --- a/app/helpers/mirror_helper.rb +++ b/app/helpers/mirror_helper.rb @@ -15,6 +15,11 @@ module MirrorHelper html_escape(_('Git LFS objects will be synced if LFS is %{docs_link_start}enabled for the project%{docs_link_end}. Push mirrors will %{strong_open}not%{strong_close} sync LFS objects over SSH.')) % { docs_link_start: docs_link_start, docs_link_end: '</a>'.html_safe, strong_open: '<strong>'.html_safe, strong_close: '</strong>'.html_safe } end + + def mirrored_repositories_count + count = @project.mirror == true ? 1 : 0 + count + @project.remote_mirrors.to_a.count(&:enabled) + end end MirrorHelper.prepend_mod_with('MirrorHelper') diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index 9f3832178aa..9824e88ca30 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -40,6 +40,7 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord vertex_project ], remove_with: '16.3', remove_after: '2023-07-22' ignore_column :dashboard_notification_limit, remove_with: '16.5', remove_after: '2023-08-22' + ignore_column :database_apdex_settings, remove_with: '16.4', remove_after: '2023-08-22' INSTANCE_REVIEW_MIN_USERS = 50 GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \ @@ -733,7 +734,7 @@ class ApplicationSetting < MainClusterwide::ApplicationRecord validates :inactive_projects_send_warning_email_after_months, numericality: { only_integer: true, greater_than: 0, less_than: :inactive_projects_delete_after_months } - validates :database_apdex_settings, json_schema: { filename: 'application_setting_database_apdex_settings' }, allow_nil: true + validates :prometheus_alert_db_indicators_settings, json_schema: { filename: 'application_setting_prometheus_alert_db_indicators_settings' }, allow_nil: true validates :namespace_aggregation_schedule_lease_duration_in_seconds, numericality: { only_integer: true, greater_than: 0 } diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index ab63a4dbb47..995fabf0137 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -984,6 +984,18 @@ class MergeRequest < ApplicationRecord branch_merge_base_commit.try(:sha) end + def existing_mrs_targeting_same_branch + similar_mrs = target_project + .merge_requests + .where(source_branch: source_branch, target_branch: target_branch) + .where(source_project: source_project) + .opened + + similar_mrs = similar_mrs.id_not_in(id) if persisted? + + similar_mrs + end + def validate_branches return unless target_project && source_project @@ -995,25 +1007,24 @@ class MergeRequest < ApplicationRecord [:source_branch, :target_branch].each { |attr| validate_branch_name(attr) } if opened? - similar_mrs = target_project - .merge_requests - .where(source_branch: source_branch, target_branch: target_branch) - .where(source_project_id: source_project&.id) - .opened - - similar_mrs = similar_mrs.where.not(id: id) if persisted? - - conflict = similar_mrs.first + conflicting_mr = existing_mrs_targeting_same_branch.first - if conflict.present? + if conflicting_mr errors.add( :validate_branches, - "Another open merge request already exists for this source branch: #{conflict.to_reference}" + conflicting_mr_message(conflicting_mr) ) end end end + def conflicting_mr_message(conflicting_mr) + format( + _("Another open merge request already exists for this source branch: %{conflicting_mr_reference}"), + conflicting_mr_reference: conflicting_mr.to_reference + ) + end + def validate_branch_name(attr) return unless will_save_change_to_attribute?(attr) diff --git a/app/validators/json_schemas/application_setting_database_apdex_settings.json b/app/validators/json_schemas/application_setting_prometheus_alert_db_indicators_settings.json index 8b58dd44586..ccd18f3d678 100644 --- a/app/validators/json_schemas/application_setting_database_apdex_settings.json +++ b/app/validators/json_schemas/application_setting_prometheus_alert_db_indicators_settings.json @@ -1,5 +1,5 @@ { - "description": "Database Apdex Settings", + "description": "Prometheus Alert Based Db indicators Settings", "type": "object", "properties": { "prometheus_api_url": { diff --git a/app/views/projects/merge_requests/creations/new.html.haml b/app/views/projects/merge_requests/creations/new.html.haml index 726dc87105c..c7569dce266 100644 --- a/app/views/projects/merge_requests/creations/new.html.haml +++ b/app/views/projects/merge_requests/creations/new.html.haml @@ -5,7 +5,11 @@ - add_page_specific_style 'page_bundles/ci_status' - add_page_specific_style 'page_bundles/merge_request' -- if @merge_request.can_be_created && !params[:change_branches] +- conflicting_mr = @merge_request.existing_mrs_targeting_same_branch.first + +- if @merge_request.can_be_created && !params[:change_branches] && !conflicting_mr = render 'new_submit' - else + - if conflicting_mr + - flash[:alert] = @merge_request.conflicting_mr_message(conflicting_mr) = render 'new_compare' diff --git a/app/views/projects/mirrors/_mirror_repos.html.haml b/app/views/projects/mirrors/_mirror_repos.html.haml index 110bc8d82f8..a9a2ca6a494 100644 --- a/app/views/projects/mirrors/_mirror_repos.html.haml +++ b/app/views/projects/mirrors/_mirror_repos.html.haml @@ -12,28 +12,45 @@ = _('Set up your project to automatically push and/or pull changes to/from another repository. Branches, tags, and commits will be synced automatically.') = link_to _('How do I mirror repositories?'), help_page_path('user/project/repository/mirror/index.md'), target: '_blank', rel: 'noopener noreferrer' - .settings-content - - if mirror_settings_enabled - = gitlab_ui_form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'new-password', data: mirrors_form_data_attributes } do |f| - .panel.panel-default - .panel-body - %div= form_errors(@project) - - .form-group.has-feedback - = label_tag :url, _('Git repository URL'), class: 'label-light' - = text_field_tag :url, nil, class: 'form-control gl-form-input js-mirror-url js-repo-url', placeholder: _('Input the remote repository URL'), required: true, pattern: "(#{protocols}):\/\/.+", autocomplete: 'new-password', data: { qa_selector: 'mirror_repository_url_field' } - - = render 'projects/mirrors/instructions' - - = render 'projects/mirrors/mirror_repos_form', f: f - = render 'projects/mirrors/branch_filter' - - .panel-footer - = f.submit _('Mirror repository'), class: 'js-mirror-submit', name: :update_remote_mirror, pajamas_button: true, data: { qa_selector: 'mirror_repository_button' } - - else - = render Pajamas::AlertComponent.new(dismissible: false) do |c| - - c.with_body do - = _('Mirror settings are only available to GitLab administrators.') - - = render 'projects/mirrors/mirror_repos_list' + .settings-content + = render Pajamas::CardComponent.new(card_options: { class: 'gl-new-card js-toggle-container' }, header_options: { class: 'gl-new-card-header' }, body_options: { class: 'gl-new-card-body gl-px-0' }) do |c| + - c.with_header do + .gl-new-card-title-wrapper + %h5.gl-new-card-title + = _('Mirrored repositories') + .gl-new-card-count + = sprite_icon('earth', css_class: 'gl-mr-2') + %span.js-mirrored-repo-count + = mirrored_repositories_count + .gl-new-card-actions + = render Pajamas::ButtonComponent.new(size: :small, button_options: { class: "js-toggle-button js-toggle-content" }) do + = _('Add new') + - c.with_body do + - if mirror_settings_enabled + .gl-new-card-add-form.gl-m-3.gl-mb-4.gl-display-none.js-toggle-content + %h4.gl-mt-0 + = s_('Profiles|Add new mirror repository') + = gitlab_ui_form_for @project, url: project_mirror_path(@project), html: { class: 'gl-show-field-errors js-mirror-form', autocomplete: 'new-password', data: mirrors_form_data_attributes } do |f| + %div= form_errors(@project) + .form-group.has-feedback + = label_tag :url, _('Git repository URL'), class: 'label-light' + = text_field_tag :url, nil, class: 'form-control gl-form-input js-mirror-url js-repo-url', placeholder: _('Input the remote repository URL'), required: true, pattern: "(#{protocols}):\/\/.+", autocomplete: 'new-password', data: { qa_selector: 'mirror_repository_url_field' } + + = render 'projects/mirrors/instructions' + + = render 'projects/mirrors/mirror_repos_form', f: f + + = render 'projects/mirrors/branch_filter' + + = f.submit _('Mirror repository'), class: 'js-mirror-submit', name: :update_remote_mirror, pajamas_button: true, data: { qa_selector: 'mirror_repository_button' } + + = render Pajamas::ButtonComponent.new(button_options: { type: 'reset', class: 'gl-ml-2 js-toggle-button' }) do + = _('Cancel') + + - else + = render Pajamas::AlertComponent.new(dismissible: false) do |c| + - c.with_body do + = _('Mirror settings are only available to GitLab administrators.') + + = render 'projects/mirrors/mirror_repos_list' diff --git a/app/views/projects/mirrors/_mirror_repos_list.html.haml b/app/views/projects/mirrors/_mirror_repos_list.html.haml index 185d86245c5..0debd13709d 100644 --- a/app/views/projects/mirrors/_mirror_repos_list.html.haml +++ b/app/views/projects/mirrors/_mirror_repos_list.html.haml @@ -1,49 +1,41 @@ - mirror_settings_enabled = can?(current_user, :admin_remote_mirror, @project) -.panel.panel-default - .table-responsive - - if !@project.mirror? && @project.remote_mirrors.count == 0 - = render Pajamas::CardComponent.new(card_options: { class: 'gl-mt-5' }) do |c| - - c.with_header do - %strong - = _('Mirrored repositories') + ' (0)' - - c.with_body do - = _('There are currently no mirrored repositories.') - - else - %table.table.gl-table.gl-mt-5 - %thead - %tr - %th - = _('Mirrored repositories') - = render_if_exists 'projects/mirrors/mirrored_repositories_count' - %th= _('Direction') - %th= _('Last update attempt') - %th= _('Last successful update') - %th - %th - %tbody.js-mirrors-table-body - = render_if_exists 'projects/mirrors/table_pull_row' - - @project.remote_mirrors.each_with_index do |mirror, index| - - next if mirror.new_record? - %tr.rspec-mirrored-repository-row{ class: ('bg-secondary' if mirror.disabled?), data: { qa_selector: 'mirrored_repository_row_container' } } - %td{ data: { qa_selector: 'mirror_repository_url_content' } } - = mirror.safe_url || _('Invalid URL') - = render_if_exists 'projects/mirrors/mirror_branches_setting_badge', record: mirror - %td= _('Push') - %td - = mirror.last_update_started_at.present? ? time_ago_with_tooltip(mirror.last_update_started_at) : _('Never') - %td{ data: { qa_selector: 'mirror_last_update_at_content' } }= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never') - %td - - if mirror.disabled? - = render 'projects/mirrors/disabled_mirror_badge' - - if mirror.last_error.present? - = gl_badge_tag _('Error'), { variant: :danger }, { data: { toggle: 'tooltip', html: 'true', qa_selector: 'mirror_error_badge_content' }, title: html_escape(mirror.last_error.try(:strip)) } - %td.gl-display-flex - - if mirror_settings_enabled - .btn-group.mirror-actions-group{ role: 'group' } - - if mirror.ssh_key_auth? - = clipboard_button(text: mirror.ssh_public_key, class: 'gl-button btn btn-default btn-icon', title: _('Copy SSH public key'), qa_selector: 'copy_public_key_button') - = render 'shared/remote_mirror_update_button', remote_mirror: mirror - = render Pajamas::ButtonComponent.new(variant: :danger, - icon: 'remove', - button_options: { class: 'js-delete-mirror rspec-delete-mirror', title: _('Remove'), data: { mirror_id: mirror.id, toggle: 'tooltip', container: 'body' } }) +.table-responsive.gl-mb-0 + - if !@project.mirror? && @project.remote_mirrors.count == 0 + .gl-new-card-empty.gl-px-5.gl-py-4= _('There are currently no mirrored repositories.') + - else + %table.table.b-table.gl-table.b-table-stacked-md + %thead.d-none.d-md-table-header-group + %tr + %th= _('Repository') + %th= _('Direction') + %th= _('Last update attempt') + %th= _('Last successful update') + %th + %th + %tbody.js-mirrors-table-body + = render_if_exists 'projects/mirrors/table_pull_row' + - @project.remote_mirrors.each_with_index do |mirror, index| + - next if mirror.new_record? + %tr.rspec-mirrored-repository-row{ class: ('bg-secondary' if mirror.disabled?), data: { qa_selector: 'mirrored_repository_row_container' } } + %td{ data: { qa_selector: 'mirror_repository_url_content' } } + = mirror.safe_url || _('Invalid URL') + = render_if_exists 'projects/mirrors/mirror_branches_setting_badge', record: mirror + %td= _('Push') + %td + = mirror.last_update_started_at.present? ? time_ago_with_tooltip(mirror.last_update_started_at) : _('Never') + %td{ data: { qa_selector: 'mirror_last_update_at_content' } }= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never') + %td + - if mirror.disabled? + = render 'projects/mirrors/disabled_mirror_badge' + - if mirror.last_error.present? + = gl_badge_tag _('Error'), { variant: :danger }, { data: { toggle: 'tooltip', html: 'true', qa_selector: 'mirror_error_badge_content' }, title: html_escape(mirror.last_error.try(:strip)) } + %td + - if mirror_settings_enabled + .btn-group.mirror-actions-group{ role: 'group' } + - if mirror.ssh_key_auth? + = clipboard_button(text: mirror.ssh_public_key, class: 'gl-button btn btn-default btn-icon', title: _('Copy SSH public key'), qa_selector: 'copy_public_key_button') + = render 'shared/remote_mirror_update_button', remote_mirror: mirror + = render Pajamas::ButtonComponent.new(variant: :danger, + icon: 'remove', + button_options: { class: 'js-delete-mirror rspec-delete-mirror', title: _('Remove'), data: { mirror_id: mirror.id, toggle: 'tooltip', container: 'body' } }) diff --git a/app/views/projects/settings/ci_cd/show.html.haml b/app/views/projects/settings/ci_cd/show.html.haml index 703f057d9a5..19f5ae4e6b3 100644 --- a/app/views/projects/settings/ci_cd/show.html.haml +++ b/app/views/projects/settings/ci_cd/show.html.haml @@ -73,7 +73,7 @@ = _("Pipeline triggers") = render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do = expanded ? _('Collapse') : _('Expand') - %p + %p.gl-text-secondary = _("Trigger a pipeline for a branch or tag by generating a trigger token and using it with an API call. The token impersonates a user's project access and permissions.") = link_to _('Learn more.'), help_page_path('ci/triggers/index'), target: '_blank', rel: 'noopener noreferrer' .settings-content |