diff options
29 files changed, 399 insertions, 49 deletions
diff --git a/app/assets/javascripts/confirm_danger_modal.js b/app/assets/javascripts/confirm_danger_modal.js index 1000c310e35..262d501bfba 100644 --- a/app/assets/javascripts/confirm_danger_modal.js +++ b/app/assets/javascripts/confirm_danger_modal.js @@ -1,39 +1,54 @@ import $ from 'jquery'; import { rstrip } from './lib/utils/common_utils'; -function openConfirmDangerModal($form, text) { - const $input = $('.js-confirm-danger-input'); +function openConfirmDangerModal($form, $modal, text) { + const $input = $('.js-confirm-danger-input', $modal); $input.val(''); - $('.js-confirm-text').text(text || ''); - $('#modal-confirm-danger').modal('show'); + $('.js-confirm-text', $modal).text(text || ''); + $modal.modal('show'); - const confirmTextMatch = $('.js-confirm-danger-match').text(); - const $submit = $('.js-confirm-danger-submit'); + const confirmTextMatch = $('.js-confirm-danger-match', $modal).text(); + const $submit = $('.js-confirm-danger-submit', $modal); $submit.disable(); $input.focus(); - $('.js-confirm-danger-input') - .off('input') - .on('input', function handleInput() { - const confirmText = rstrip($(this).val()); - if (confirmText === confirmTextMatch) { - $submit.enable(); - } else { - $submit.disable(); - } - }); - $('.js-confirm-danger-submit') + $input.off('input').on('input', function handleInput() { + const confirmText = rstrip($(this).val()); + if (confirmText === confirmTextMatch) { + $submit.enable(); + } else { + $submit.disable(); + } + }); + $('.js-confirm-danger-submit', $modal) .off('click') .on('click', () => $form.submit()); } +function getModal($btn) { + const $modal = $btn.prev('.modal'); + + if ($modal.length) { + return $modal; + } + + return $('#modal-confirm-danger'); +} + export default function initConfirmDangerModal() { $(document).on('click', '.js-confirm-danger', e => { - e.preventDefault(); const $btn = $(e.target); - const $form = $btn.closest('form'); - const text = $btn.data('confirmDangerMessage'); - openConfirmDangerModal($form, text); + const checkFieldName = $btn.data('checkFieldName'); + const checkFieldCompareValue = $btn.data('checkCompareValue'); + const checkFieldVal = parseInt($(`[name="${checkFieldName}"]`).val(), 10); + + if (!checkFieldName || checkFieldVal < checkFieldCompareValue) { + e.preventDefault(); + const $form = $btn.closest('form'); + const $modal = getModal($btn); + const text = $btn.data('confirmDangerMessage'); + openConfirmDangerModal($form, $modal, text); + } }); } 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 aa65b16a3c3..65e523715e2 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 @@ -3,8 +3,9 @@ import _ from 'underscore'; import successSvg from 'icons/_icon_status_success.svg'; import warningSvg from 'icons/_icon_status_warning.svg'; import simplePoll from '~/lib/utils/simple_poll'; -import { __ } from '~/locale'; +import { __, sprintf } from '~/locale'; import readyToMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/ready_to_merge'; +import { GlIcon } from '@gitlab/ui'; import MergeRequest from '../../../merge_request'; import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests'; import Flash from '../../../flash'; @@ -24,6 +25,7 @@ export default { CommitsHeader, CommitEdit, CommitMessageDropdown, + GlIcon, }, mixins: [readyToMergeMixin], props: { @@ -111,6 +113,18 @@ export default { shouldShowMergeEdit() { return !this.mr.ffOnlyEnabled; }, + shaMismatchLink() { + const href = this.mr.mergeRequestDiffsPath; + + return sprintf( + __('New changes were added. %{linkStart}Reload the page to review them%{linkEnd}'), + { + linkStart: `<a href="${href}">`, + linkEnd: '</a>', + }, + false, + ); + }, }, methods: { updateMergeCommitMessage(includeDescription) { @@ -123,7 +137,7 @@ export default { } const options = { - sha: this.mr.sha, + sha: this.mr.latestSHA || this.mr.sha, commit_message: this.commitMessage, auto_merge_strategy: useAutoMerge ? this.mr.preferredAutoMergeStrategy : undefined, should_remove_source_branch: this.removeSourceBranch === true, @@ -314,6 +328,10 @@ export default { </template> </div> </div> + <div v-if="mr.isSHAMismatch" class="d-flex align-items-center mt-2 js-sha-mismatch"> + <gl-icon name="warning-solid" class="text-warning mr-1" /> + <span class="text-warning" v-html="shaMismatchLink"></span> + </div> </div> </div> <template v-if="shouldShowMergeControls"> diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index 31dbddbd21a..4861588992c 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -25,7 +25,6 @@ import NothingToMergeState from './components/states/nothing_to_merge.vue'; import MissingBranchState from './components/states/mr_widget_missing_branch.vue'; import NotAllowedState from './components/states/mr_widget_not_allowed.vue'; import ReadyToMergeState from './components/states/ready_to_merge.vue'; -import ShaMismatchState from './components/states/sha_mismatch.vue'; import UnresolvedDiscussionsState from './components/states/unresolved_discussions.vue'; import PipelineBlockedState from './components/states/mr_widget_pipeline_blocked.vue'; import PipelineFailedState from './components/states/pipeline_failed.vue'; @@ -63,7 +62,7 @@ export default { 'mr-widget-not-allowed': NotAllowedState, 'mr-widget-missing-branch': MissingBranchState, 'mr-widget-ready-to-merge': ReadyToMergeState, - 'sha-mismatch': ShaMismatchState, + 'sha-mismatch': ReadyToMergeState, 'mr-widget-checking': CheckingState, 'mr-widget-unresolved-discussions': UnresolvedDiscussionsState, 'mr-widget-pipeline-blocked': PipelineBlockedState, diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index 7e5b8f14b96..4c178950ec1 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -48,6 +48,7 @@ export default class MergeRequestStore { this.commits = data.commits_without_merge_commits || []; this.squashCommitMessage = data.default_squash_commit_message; this.rebaseInProgress = data.rebase_in_progress; + this.mergeRequestDiffsPath = data.diffs_path; if (data.issues_links) { const links = data.issues_links; @@ -81,6 +82,7 @@ export default class MergeRequestStore { this.isOpen = data.state === 'opened'; this.hasMergeableDiscussionsState = data.mergeable_discussions_state === false; this.isSHAMismatch = this.sha !== data.diff_head_sha; + this.latestSHA = data.diff_head_sha; this.canBeMerged = data.can_be_merged || false; this.isMergeAllowed = data.mergeable || false; this.mergeOngoing = data.merge_ongoing; @@ -170,6 +172,7 @@ export default class MergeRequestStore { this.conflictsDocsPath = data.conflicts_docs_path; this.ciEnvironmentsStatusPath = data.ci_environments_status_path; this.securityApprovalsHelpPagePath = data.security_approvals_help_page_path; + this.eligibleApproversDocsPath = data.eligible_approvers_docs_path; } get isNothingToMergeState() { diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index ff013f3f7ec..c7fdd009edb 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -699,4 +699,8 @@ module ProjectsHelper def vue_file_list_enabled? Feature.enabled?(:vue_file_list, @project) end + + def show_visibility_confirm_modal?(project) + project.unlink_forks_upon_visibility_decrease_enabled? && project.visibility_level > Gitlab::VisibilityLevel::PRIVATE && project.forks_count > 0 + end end diff --git a/app/serializers/merge_request_poll_cached_widget_entity.rb b/app/serializers/merge_request_poll_cached_widget_entity.rb index c7dc8c43a9a..2f8eb6650e8 100644 --- a/app/serializers/merge_request_poll_cached_widget_entity.rb +++ b/app/serializers/merge_request_poll_cached_widget_entity.rb @@ -70,6 +70,10 @@ class MergeRequestPollCachedWidgetEntity < IssuableEntity presenter(merge_request).source_branch_with_namespace_link end + expose :diffs_path do |merge_request| + diffs_project_merge_request_path(merge_request.project, merge_request) + end + private delegate :current_user, to: :request diff --git a/app/views/projects/_visibility_modal.html.haml b/app/views/projects/_visibility_modal.html.haml new file mode 100644 index 00000000000..3ef93a40137 --- /dev/null +++ b/app/views/projects/_visibility_modal.html.haml @@ -0,0 +1,30 @@ +- strong_start = "<strong>".html_safe +- strong_end = "</strong>".html_safe + +.modal.js-confirm-project-visiblity{ tabindex: -1 } + .modal-dialog + .modal-content + .modal-header + %h3.page-title= _('Reduce this project’s visibility?') + %button.close{ type: "button", "data-dismiss": "modal", "aria-label" => _('Close') } + %span{ "aria-hidden": true }= sprite_icon("close", size: 16) + .modal-body + %p + - if @project.group + = _("You're about to reduce the visibility of the project %{strong_start}%{project_name}%{strong_end} in %{strong_start}%{group_name}%{strong_end}.").html_safe % { project_name: @project.name, group_name: @project.group.name, strong_start: strong_start, strong_end: strong_end } + - else + = _("You're about to reduce the visibility of the project %{strong_start}%{project_name}%{strong_end}.").html_safe % { project_name: @project.name, strong_start: strong_start, strong_end: strong_end } + %p + = _('Once you confirm and press "Reduce project visibility":') + %ul + %li + = ("Current forks will keep their visibility level but their fork relationship with this project will be %{strong_start}removed%{strong_end}.").html_safe % { strong_start: strong_start, strong_end: strong_end } + %label{ for: "confirm_path_input" } + = ("To confirm, type %{phrase_code}").html_safe % { phrase_code: '<code class="js-confirm-danger-match">%{phrase_name}</code>'.html_safe % { phrase_name: @project.full_path } } + .form-group + = text_field_tag 'confirm_path_input', '', class: 'form-control js-confirm-danger-input qa-confirm-input' + .form-actions.clearfix + .pull-right + %button.btn.btn-default{ type: "button", "data-dismiss": "modal" } + = _('Cancel') + = submit_tag _('Reduce project visibility'), class: "btn btn-danger js-confirm-danger-submit qa-confirm-button", disabled: true diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 328fdd0be10..7ad52673137 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -21,7 +21,9 @@ %input{ name: 'update_section', type: 'hidden', value: 'js-shared-permissions' } %template.js-project-permissions-form-data{ type: "application/json" }= project_permissions_panel_data_json(@project) .js-project-permissions-form - = f.submit _('Save changes'), class: "btn btn-success", data: { qa_selector: 'visibility_features_permissions_save_button' } + - if show_visibility_confirm_modal?(@project) + = render "visibility_modal" + = f.submit _('Save changes'), class: "btn btn-success #{('js-confirm-danger' if show_visibility_confirm_modal?(@project))}", data: { qa_selector: 'visibility_features_permissions_save_button', check_field_name: ("project[visibility_level]" if show_visibility_confirm_modal?(@project)), check_compare_value: @project.visibility_level } %section.qa-merge-request-settings.rspec-merge-request-settings.settings.merge-requests-feature.no-animate#js-merge-request-settings{ class: [('expanded' if expanded), ('hidden' if @project.project_feature.send(:merge_requests_access_level) == 0)] } .settings-header diff --git a/app/views/projects/merge_requests/_widget.html.haml b/app/views/projects/merge_requests/_widget.html.haml index f056189fec0..b00c95c3cd7 100644 --- a/app/views/projects/merge_requests/_widget.html.haml +++ b/app/views/projects/merge_requests/_widget.html.haml @@ -9,5 +9,6 @@ window.gl.mrWidgetData.squash_before_merge_help_path = '#{help_page_path("user/project/merge_requests/squash_and_merge")}'; window.gl.mrWidgetData.troubleshooting_docs_path = '#{help_page_path('user/project/merge_requests/index.md', anchor: 'troubleshooting')}'; window.gl.mrWidgetData.security_approvals_help_page_path = '#{help_page_path('user/application_security/index.html', anchor: 'security-approvals-in-merge-requests-ultimate')}'; + window.gl.mrWidgetData.eligible_approvers_docs_path = '#{help_page_path('user/project/merge_requests/merge_request_approvals', anchor: 'eligible-approvers')}'; #js-vue-mr-widget.mr-widget diff --git a/changelogs/unreleased/11506-add-additional-attributes-and-arguments-for-the-graphql-epic-query.yml b/changelogs/unreleased/11506-add-additional-attributes-and-arguments-for-the-graphql-epic-query.yml new file mode 100644 index 00000000000..a3bf40ce76f --- /dev/null +++ b/changelogs/unreleased/11506-add-additional-attributes-and-arguments-for-the-graphql-epic-query.yml @@ -0,0 +1,5 @@ +--- +title: Add upvote/downvotes attributes to GraphQL Epic query +merge_request: 14311 +author: +type: added diff --git a/changelogs/unreleased/31236-auto-update-merge-widget.yml b/changelogs/unreleased/31236-auto-update-merge-widget.yml new file mode 100644 index 00000000000..b84e3e2c15b --- /dev/null +++ b/changelogs/unreleased/31236-auto-update-merge-widget.yml @@ -0,0 +1,5 @@ +--- +title: Allow merge without refresh when new commits are pushed +merge_request: 19725 +author: +type: changed diff --git a/changelogs/unreleased/37952-fix-missing-empty-rule-ux-in-mr-widget.yml b/changelogs/unreleased/37952-fix-missing-empty-rule-ux-in-mr-widget.yml new file mode 100644 index 00000000000..caec491fa67 --- /dev/null +++ b/changelogs/unreleased/37952-fix-missing-empty-rule-ux-in-mr-widget.yml @@ -0,0 +1,5 @@ +--- +title: Resolve Add missing popover and remove none in MR widget +merge_request: 21095 +author: +type: other diff --git a/changelogs/unreleased/ph-33358-projectVisibilityConfirmModal.yml b/changelogs/unreleased/ph-33358-projectVisibilityConfirmModal.yml new file mode 100644 index 00000000000..2f9e2e76cff --- /dev/null +++ b/changelogs/unreleased/ph-33358-projectVisibilityConfirmModal.yml @@ -0,0 +1,5 @@ +--- +title: Asks for confirmation before changing project visibility level +merge_request: 20170 +author: +type: added diff --git a/changelogs/unreleased/remove-action-buttons-if-no-designs.yml b/changelogs/unreleased/remove-action-buttons-if-no-designs.yml new file mode 100644 index 00000000000..876ce5e5f00 --- /dev/null +++ b/changelogs/unreleased/remove-action-buttons-if-no-designs.yml @@ -0,0 +1,5 @@ +--- +title: Remove action buttons from designs tab if there are no designs +merge_request: 21186 +author: +type: fixed diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index 6852e0016b0..8abe501eafc 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -1230,6 +1230,11 @@ type Epic implements Noteable { """ last: Int ): DiscussionConnection! + + """ + Number of downvotes the epic has received + """ + downvotes: Int! dueDate: Time dueDateFixed: Time dueDateFromMilestones: Time @@ -1361,6 +1366,11 @@ type Epic implements Noteable { updatedAt: Time """ + Number of upvotes the epic has received + """ + upvotes: Int! + + """ Permissions for the current user on the resource """ userPermissions: EpicPermissions! diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index f2cf473c1f3..aa75d920997 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -3631,6 +3631,24 @@ "deprecationReason": null }, { + "name": "downvotes", + "description": "Number of downvotes the epic has received", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "dueDate", "description": null, "args": [ @@ -4182,6 +4200,24 @@ "deprecationReason": null }, { + "name": "upvotes", + "description": "Number of upvotes the epic has received", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "userPermissions", "description": "Permissions for the current user on the resource", "args": [ diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 095d14ed4d5..e9c27ffc432 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -211,6 +211,8 @@ The API can be explored interactively using the [GraphiQL IDE](../index.md#graph | `dueDateIsFixed` | Boolean | | | `dueDateFixed` | Time | | | `dueDateFromMilestones` | Time | | +| `upvotes` | Int! | Number of upvotes the epic has received | +| `downvotes` | Int! | Number of downvotes the epic has received | | `closedAt` | Time | | | `createdAt` | Time | | | `updatedAt` | Time | | diff --git a/doc/development/gitaly.md b/doc/development/gitaly.md index 7d3c2b8fdf8..1fa555de994 100644 --- a/doc/development/gitaly.md +++ b/doc/development/gitaly.md @@ -277,14 +277,81 @@ Here are the steps to gate a new feature in Gitaly behind a feature flag. ### GitLab Rails -1. Add feature flag to `lib/gitlab/gitaly_client.rb` (in GitLab Rails): +1. In GitLab Rails: - ```ruby - SERVER_FEATURE_FLAGS = %w[go-find-all-tags].freeze - ``` + 1. Add the feature flag to `SERVER_FEATURE_FLAGS` in `lib/feature/gitaly.rb`: + + ```ruby + SERVER_FEATURE_FLAGS = %w[go-find-all-tags].freeze + ``` + + 1. Search for `["gitaly"]["features"]` (currently in `spec/requests/api/internal/base_spec.rb`) + and fix the expected results for the tests by adding the new feature flag into it: + + ```ruby + expect(json_response["gitaly"]["features"]).to eq('gitaly-feature-get-all-lfs-pointers-go' => 'true', 'gitaly-feature-go-find-all-tags' => 'true') + ``` -1. Test in rails console by setting feature flag: +1. Test in a Rails console by setting the feature flag: + + NOTE: **Note:** + Pay attention to the name of the flag and the one used in the Rails console. + There is a difference between them (dashes replaced by underscores and name + prefix is changed). ```ruby - Feature.enable('gitaly_go-find-all-tags') + Feature.enable('gitaly_go_find_all_tags') ``` + +### Testing with GDK + +To be sure that the flag is set correctly and it goes into Gitaly, you can check +the integration by using GDK: + +1. The state of the flag must be observable. To check it, you need to enable it + by fetching the Prometheus metrics: + 1. Navigate to GDK's root directory. + 1. Make sure you have the proper branch checked out for Gitaly. + 1. Recompile it with `make gitaly-setup` and restart the service with `gdk restart gitaly`. + 1. Make sure your setup is runnig: `gdk status | grep praefect`. + 1. Check what config file is used: `cat ./services/praefect/run | grep praefect` value of the `-config` flag + 1. Uncomment `prometheus_listen_addr` in the configuration file and run `gdk restart gitaly`. + +1. Make sure that the flag is not enabled yet: + 1. Perform whatever action is required to trigger your changes (project creation, + submitting commit, observing history, etc.). + 1. Check that the list of current metrics has the new counter for the feature flag: + + ```sh + curl --silent http://localhost:9236/metrics | grep go_find_all_tags + ``` + +1. Once you observe the metrics for the new feature flag and it increments, you + can enable the new feature: + 1. Navigate to GDK's root directory. + 1. Start a Rails console: + + ```sh + bundle install && bundle exec rails console + ``` + + 1. Check the list of feature flags: + + ```ruby + Feature::Gitaly.server_feature_flags + ``` + + It should be disabled `"gitaly-feature-go-find-all-tags"=>"false"`. + 1. Enable it: + + ```ruby + Feature.enable('gitaly_go_find_all_tags') + ``` + + 1. Exit the Rails console and perform whatever action is required to trigger + your changes (project creation, submitting commit, observing history, etc.). + 1. Verify the feature is on by observing the metrics for it: + + ```sh + curl --silent http://localhost:9236/metrics | grep go_find_all_tags + ``` diff --git a/doc/public_access/img/project_visibility_confirmation_v12_6.png b/doc/public_access/img/project_visibility_confirmation_v12_6.png Binary files differnew file mode 100644 index 00000000000..ac4d70ff11a --- /dev/null +++ b/doc/public_access/img/project_visibility_confirmation_v12_6.png diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md index bb19436017a..1b6895aaef1 100644 --- a/doc/public_access/public_access.md +++ b/doc/public_access/public_access.md @@ -77,6 +77,16 @@ by accident. The restricted visibility settings do not apply to admin users. For details, see [Restricted visibility levels](../user/admin_area/settings/visibility_and_access_controls.md#restricted-visibility-levels). +## Reducing visibility + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/33358) in GitLab 12.6. + +Reducing a project's visibility level will remove the fork relationship between the project and +any forked project. This is a potentially destructive action which requires confirmation before +this can be saved. + +![Project visibility change confirmation](img/project_visibility_confirmation_v12_6.png) + <!-- ## Troubleshooting Include any troubleshooting steps that you can foresee. If you know beforehand what issues diff --git a/doc/user/project/settings/index.md b/doc/user/project/settings/index.md index 810bd2a5937..2c7a24da8f9 100644 --- a/doc/user/project/settings/index.md +++ b/doc/user/project/settings/index.md @@ -26,6 +26,10 @@ Set up your project's access, [visibility](../../../public_access/public_access. ![projects sharing permissions](img/sharing_and_permissions_settings_v12_3.png) +CAUTION: **Caution:** +[Reducing a project's visibility level](../../../public_access/public_access.md#reducing-visibility) +will remove the fork relationship between the project and any forked project. + If Issues are disabled, or you can't access Issues because you're not a project member, then Labels and Milestones links will be missing from the sidebar UI. diff --git a/lib/tasks/gitlab/import_export/import.rake b/lib/tasks/gitlab/import_export/import.rake index d15749d8285..f999a05875d 100644 --- a/lib/tasks/gitlab/import_export/import.rake +++ b/lib/tasks/gitlab/import_export/import.rake @@ -17,6 +17,10 @@ namespace :gitlab do task :import, [:username, :namespace_path, :project_path, :archive_path] => :gitlab_environment do |_t, args| warn_user_is_not_gitlab + if ENV['IMPORT_DEBUG'].present? + ActiveRecord::Base.logger = Logger.new(STDOUT) + end + GitlabProjectImport.new( namespace_path: args.namespace_path, project_path: args.project_path, diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 62f1a4c3a14..783766f3406 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -11461,6 +11461,9 @@ msgstr "" msgid "New branch unavailable" msgstr "" +msgid "New changes were added. %{linkStart}Reload the page to review them%{linkEnd}" +msgstr "" + msgid "New deploy key" msgstr "" @@ -11989,6 +11992,9 @@ msgstr "" msgid "Once the exported file is ready, you will receive a notification email with a download link, or you can download it from this page." msgstr "" +msgid "Once you confirm and press \"Reduce project visibility\":" +msgstr "" + msgid "One more item" msgid_plural "%d more items" msgstr[0] "" @@ -14397,6 +14403,12 @@ msgstr "" msgid "Redirect to SAML provider to test configuration" msgstr "" +msgid "Reduce project visibility" +msgstr "" + +msgid "Reduce this project’s visibility?" +msgstr "" + msgid "Reference:" msgstr "" @@ -20401,6 +20413,12 @@ msgstr "" msgid "You'll need to use different branch names to get a valid comparison." msgstr "" +msgid "You're about to reduce the visibility of the project %{strong_start}%{project_name}%{strong_end} in %{strong_start}%{group_name}%{strong_end}." +msgstr "" + +msgid "You're about to reduce the visibility of the project %{strong_start}%{project_name}%{strong_end}." +msgstr "" + msgid "You're not allowed to %{tag_start}edit%{tag_end} files in this project directly. Please fork this project, make your changes there, and submit a merge request." msgstr "" diff --git a/package.json b/package.json index 92675b526f0..c090c6c0359 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,8 @@ "@babel/plugin-syntax-dynamic-import": "^7.2.0", "@babel/plugin-syntax-import-meta": "^7.2.0", "@babel/preset-env": "^7.6.2", - "@gitlab/svgs": "^1.82.0", - "@gitlab/ui": "8.0.1", + "@gitlab/svgs": "^1.85.0", + "@gitlab/ui": "8.2.0", "@gitlab/visual-review-tools": "1.2.0", "@sentry/browser": "^5.7.1", "@sourcegraph/code-host-integration": "^0.0.14", @@ -140,7 +140,7 @@ }, "devDependencies": { "@babel/plugin-transform-modules-commonjs": "^7.5.0", - "@gitlab/eslint-config": "^1.6.0", + "@gitlab/eslint-config": "^2.0.0", "@gitlab/eslint-plugin-i18n": "^1.1.0", "@gitlab/eslint-plugin-vue-i18n": "^1.2.0", "@vue/test-utils": "^1.0.0-beta.25", diff --git a/spec/features/projects/clusters/gcp_spec.rb b/spec/features/projects/clusters/gcp_spec.rb index 9e1b4c5c45f..ba7374d5040 100644 --- a/spec/features/projects/clusters/gcp_spec.rb +++ b/spec/features/projects/clusters/gcp_spec.rb @@ -191,4 +191,28 @@ describe 'Gcp Cluster', :js do expect(page).not_to have_css('.gcp-signup-offer') end end + + context 'when third party offers are disabled' do + let(:admin) { create(:admin) } + before do + stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false') + sign_in(admin) + gitlab_enable_admin_mode_sign_in(admin) + visit integrations_admin_application_settings_path + end + + it 'user does not see the offer' do + page.within('.as-third-party-offers') do + click_button 'Expand' + check 'Do not display offers from third parties within GitLab' + click_button 'Save changes' + end + + expect(page).to have_content "Application settings saved successfully" + + visit project_clusters_path(project) + + expect(page).not_to have_css('.gcp-signup-offer') + end + end end diff --git a/spec/features/projects/user_changes_project_visibility_spec.rb b/spec/features/projects/user_changes_project_visibility_spec.rb new file mode 100644 index 00000000000..31da4140d35 --- /dev/null +++ b/spec/features/projects/user_changes_project_visibility_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'User changes public project visibility', :js do + include ProjectForksHelper + + before do + fork_project(project, project.owner) + + sign_in(project.owner) + + visit edit_project_path(project) + end + + shared_examples 'changing visibility to private' do + it 'requires confirmation' do + visibility_select = first('.project-feature-controls .select-control') + visibility_select.select('Private') + + page.within('#js-shared-permissions') do + click_button 'Save changes' + end + + find('.js-confirm-danger-input').send_keys(project.path_with_namespace) + + page.within '.modal' do + click_button 'Reduce project visibility' + end + + expect(page).to have_text("Project '#{project.name}' was successfully updated") + end + end + + context 'when a project is public' do + let(:project) { create(:project, :empty_repo, :public) } + + it_behaves_like 'changing visibility to private' + end + + context 'when the project is internal' do + let(:project) { create(:project, :empty_repo, :internal) } + + it_behaves_like 'changing visibility to private' + end +end diff --git a/spec/fixtures/api/schemas/entities/merge_request_poll_cached_widget.json b/spec/fixtures/api/schemas/entities/merge_request_poll_cached_widget.json index e8959307767..b40b71d2cd6 100644 --- a/spec/fixtures/api/schemas/entities/merge_request_poll_cached_widget.json +++ b/spec/fixtures/api/schemas/entities/merge_request_poll_cached_widget.json @@ -26,6 +26,7 @@ "has_conflicts": { "type": "boolean" }, "can_be_merged": { "type": "boolean" }, "remove_source_branch": { "type": ["boolean", "null"] }, + "diffs_path": { "type": "string" }, "source_branch_exists": { "type": "boolean" }, "branch_missing": { "type": "boolean" }, "commits_without_merge_commits": { "type": "array" }, diff --git a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js index 2bb2319cc60..3a4fdaf9a20 100644 --- a/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js +++ b/spec/javascripts/vue_mr_widget/components/states/mr_widget_ready_to_merge_spec.js @@ -938,4 +938,31 @@ describe('ReadyToMerge', () => { expect(customVm.$el.querySelector('.js-modify-commit-message-button')).toBeNull(); }); }); + + describe('with a mismatched SHA', () => { + const findMismatchShaBlock = () => vm.$el.querySelector('.js-sha-mismatch'); + + beforeEach(() => { + vm = createComponent({ + mr: { + isSHAMismatch: true, + mergeRequestDiffsPath: '/merge_requests/1/diffs', + }, + }); + }); + + it('displays a warning message', () => { + expect(findMismatchShaBlock()).toExist(); + }); + + it('warns the user to refresh to review', () => { + expect(findMismatchShaBlock().textContent.trim()).toBe( + 'New changes were added. Reload the page to review them', + ); + }); + + it('displays link to the diffs tab', () => { + expect(findMismatchShaBlock().querySelector('a').href).toContain(vm.mr.mergeRequestDiffsPath); + }); + }); }); diff --git a/yarn.lock b/yarn.lock index c2f8345c0a8..5b6038b6e04 100644 --- a/yarn.lock +++ b/yarn.lock @@ -690,16 +690,16 @@ exec-sh "^0.3.2" minimist "^1.2.0" -"@gitlab/eslint-config@^1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@gitlab/eslint-config/-/eslint-config-1.6.0.tgz#1fd247d6ab477d53d4c330e05f007e3afa303689" - integrity sha512-EZffCwsRZmRWPP6N3wp20EJDVGYLG1v43/W7fF/gYQpUjcRclC8ks/jEv8UppasSDlanDmkh1bLWoE9CelSyyw== +"@gitlab/eslint-config@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@gitlab/eslint-config/-/eslint-config-2.0.0.tgz#e30dbf2b170a7a4ca003a321de9f4170a2512510" + integrity sha512-3Zw3ww8Q4hhVYxO7vliByD0yTeAQn4iBxOyqlASAZepZgdu/OmM4NPbWyntpTfDyHGoRGxmzEaCqv7DS6ubACA== dependencies: babel-eslint "^10.0.1" eslint-config-airbnb-base "^13.1.0" eslint-config-prettier "^3.3.0" eslint-plugin-filenames "^1.3.2" - eslint-plugin-import "^2.16.0" + eslint-plugin-import "^2.18.2" eslint-plugin-promise "^4.1.1" eslint-plugin-vue "^5.0.0" @@ -717,15 +717,15 @@ dependencies: vue-eslint-parser "^6.0.4" -"@gitlab/svgs@^1.82.0": - version "1.82.0" - resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.82.0.tgz#c059c460afc13ebfe9df370521ca8963fa5afb80" - integrity sha512-9L4Brys2LCk44lHvFsCFDKN768lYjoMVYDb4PD7FSjqUEruQQ1SRj0rvb1RWKLhiTCDKrtDOXkH6I1TTEms24w== +"@gitlab/svgs@^1.85.0": + version "1.85.0" + resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.85.0.tgz#c80247ec4764824385df2837136a0d4a84f881dc" + integrity sha512-yzvKut0MPJEbSx/LExopCLpF5KEZsckF+d/Blbji1VqODVanH85oIVuJNmdECUlc7qxye9Or3evpFjW9Pkshmg== -"@gitlab/ui@8.0.1": - version "8.0.1" - resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-8.0.1.tgz#4e3b4791045540785cc389af931e24c6411910ca" - integrity sha512-PfZPlx3f12wcGxe0eMAXRk1gdhEAkX4czQWAt8EQ1WosKiADCNzCpEPR4jyWa60RF/+zHqJKIjq0VqLMClk8Jg== +"@gitlab/ui@8.2.0": + version "8.2.0" + resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-8.2.0.tgz#82cf512407f8a774878969c72c4227d08ce49aa6" + integrity sha512-ZzxA3XwmwZpol6QJjqBf3Oblb1wSFbDJ4QENvPiE9lAUHvhbe8wfKBK++RtgOtd6aXtLvoY1o1Du9qill07Jvg== dependencies: "@babel/standalone" "^7.0.0" "@gitlab/vue-toasted" "^1.3.0" @@ -4243,7 +4243,7 @@ eslint-plugin-filenames@^1.3.2: lodash.snakecase "4.1.1" lodash.upperfirst "4.3.1" -eslint-plugin-import@^2.16.0, eslint-plugin-import@^2.18.2: +eslint-plugin-import@^2.18.2: version "2.18.2" resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.18.2.tgz#02f1180b90b077b33d447a17a2326ceb400aceb6" integrity sha512-5ohpsHAiUBRNaBWAF08izwUGlbrJoJJ+W9/TBwsGoR1MnlgfwMIKrFeSjWbt6moabiXW9xNvtFz+97KHRfI4HQ== |