diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-05 15:09:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-05-05 15:09:42 +0000 |
commit | 0d09054d162458364ce3fc431506c182e2a5fa4f (patch) | |
tree | 876e3bd5b2aae32189c09e113454a58c91cbd3f6 | |
parent | f44809bf96e636a28394c9849dbea5f68e8c910c (diff) | |
download | gitlab-ce-0d09054d162458364ce3fc431506c182e2a5fa4f.tar.gz |
Add latest changes from gitlab-org/gitlab@master
37 files changed, 498 insertions, 389 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d9b0185a8d8..21dfd6563e2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -15,7 +15,7 @@ stages: # in cases where jobs require Docker-in-Docker, the job # definition must be extended with `.use-docker-in-docker` default: - image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-81.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34" + image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34" tags: - gitlab-org # All jobs are interruptible by default diff --git a/.gitlab/ci/frontend.gitlab-ci.yml b/.gitlab/ci/frontend.gitlab-ci.yml index 2df24eedc1f..5be4d4eaf1f 100644 --- a/.gitlab/ci/frontend.gitlab-ci.yml +++ b/.gitlab/ci/frontend.gitlab-ci.yml @@ -15,7 +15,7 @@ - .default-retry - .default-before_script - .assets-compile-cache - image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-git-2.26-lfs-2.9-chrome-81.0-node-12.x-yarn-1.21-graphicsmagick-1.3.34-docker-19.03.1 + image: registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-graphicsmagick-1.3.34-docker-19.03.1 stage: prepare variables: NODE_ENV: "production" diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml index 21d085c5ca5..0c26273ef04 100644 --- a/.gitlab/ci/global.gitlab-ci.yml +++ b/.gitlab/ci/global.gitlab-ci.yml @@ -30,7 +30,7 @@ policy: pull .use-pg9: - image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-81.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34" + image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34" services: - name: postgres:9.6.17 command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] @@ -41,7 +41,7 @@ key: "debian-stretch-ruby-2.6.6-pg9-node-12.x" .use-pg10: - image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-81.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34" + image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34" services: - name: postgres:10.12 command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] @@ -52,7 +52,7 @@ key: "debian-stretch-ruby-2.6.6-pg10-node-12.x" .use-pg11: - image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-81.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34" + image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34" services: - name: postgres:11.6 command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] @@ -63,7 +63,7 @@ key: "debian-stretch-ruby-2.6.6-pg11-node-12.x" .use-pg9-ee: - image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-81.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34" + image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-9.6-graphicsmagick-1.3.34" services: - name: postgres:9.6.17 command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] @@ -75,7 +75,7 @@ key: "debian-stretch-ruby-2.6.6-pg9-node-12.x" .use-pg10-ee: - image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-81.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34" + image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-10-graphicsmagick-1.3.34" services: - name: postgres:10.12 command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] @@ -87,7 +87,7 @@ key: "debian-stretch-ruby-2.6.6-pg10-node-12.x" .use-pg11-ee: - image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-81.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34" + image: "registry.gitlab.com/gitlab-org/gitlab-build-images:ruby-2.6.6-golang-1.14-git-2.26-lfs-2.9-chrome-73.0-node-12.x-yarn-1.21-postgresql-11-graphicsmagick-1.3.34" services: - name: postgres:11.6 command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index f2de98cbfab..c63397dceea 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -145,7 +145,6 @@ db:check-schema: - .db-job-base - .rails:rules:ee-mr-and-master-only script: - - scripts/regenerate-schema - source scripts/schema_changed.sh db:migrate-from-v11.11.0: diff --git a/app/assets/javascripts/registry/explorer/constants.js b/app/assets/javascripts/registry/explorer/constants.js index d4b9d25b212..4ca4c7088a6 100644 --- a/app/assets/javascripts/registry/explorer/constants.js +++ b/app/assets/javascripts/registry/explorer/constants.js @@ -39,14 +39,20 @@ export const DELETE_IMAGE_SUCCESS_MESSAGE = s__( // Image details page +export const DETAILS_PAGE_TITLE = s__('ContainerRegistry|%{imageName} tags'); + export const DELETE_TAG_ERROR_MESSAGE = s__( - 'ContainerRegistry|Something went wrong while deleting the tag.', + 'ContainerRegistry|Something went wrong while marking the tag for deletion.', +); +export const DELETE_TAG_SUCCESS_MESSAGE = s__( + 'ContainerRegistry|Tag successfully marked for deletion.', ); -export const DELETE_TAG_SUCCESS_MESSAGE = s__('ContainerRegistry|Tag deleted successfully'); export const DELETE_TAGS_ERROR_MESSAGE = s__( - 'ContainerRegistry|Something went wrong while deleting the tags.', + 'ContainerRegistry|Something went wrong while marking the tags for deletion.', +); +export const DELETE_TAGS_SUCCESS_MESSAGE = s__( + 'ContainerRegistry|Tags successfully marked for deletion.', ); -export const DELETE_TAGS_SUCCESS_MESSAGE = s__('ContainerRegistry|Tags deleted successfully'); export const DEFAULT_PAGE = 1; export const DEFAULT_PAGE_SIZE = 10; @@ -65,6 +71,27 @@ export const LIST_LABEL_IMAGE_ID = s__('ContainerRegistry|Image ID'); export const LIST_LABEL_SIZE = s__('ContainerRegistry|Compressed Size'); export const LIST_LABEL_LAST_UPDATED = s__('ContainerRegistry|Last Updated'); +export const REMOVE_TAG_BUTTON_TITLE = s__('ContainerRegistry|Remove tag'); +export const REMOVE_TAGS_BUTTON_TITLE = s__('ContainerRegistry|Remove selected tags'); + +export const REMOVE_TAG_CONFIRMATION_TEXT = s__( + `ContainerRegistry|You are about to remove %{item}. Are you sure?`, +); +export const REMOVE_TAGS_CONFIRMATION_TEXT = s__( + `ContainerRegistry|You are about to remove %{item} tags. Are you sure?`, +); + +export const EMPTY_IMAGE_REPOSITORY_TITLE = s__('ContainerRegistry|This image has no active tags'); +export const EMPTY_IMAGE_REPOSITORY_MESSAGE = s__( + `ContainerRegistry|The last tag related to this image was recently removed. +This empty image and any associated data will be automatically removed as part of the regular Garbage Collection process. +If you have any questions, contact your administrator.`, +); + +export const ADMIN_GARBAGE_COLLECTION_TIP = s__( + 'ContainerRegistry|Remember to run %{docLinkStart}garbage collection%{docLinkEnd} to remove the stale data from storage.', +); + // Expiration policies export const EXPIRATION_POLICY_ALERT_TITLE = s__( diff --git a/app/assets/javascripts/registry/explorer/pages/details.vue b/app/assets/javascripts/registry/explorer/pages/details.vue index 6afd4d1107a..1d4dcd9a7a2 100644 --- a/app/assets/javascripts/registry/explorer/pages/details.vue +++ b/app/assets/javascripts/registry/explorer/pages/details.vue @@ -9,12 +9,14 @@ import { GlPagination, GlModal, GlSprintf, + GlAlert, + GlLink, GlEmptyState, GlResizeObserverDirective, GlSkeletonLoader, } from '@gitlab/ui'; import { GlBreakpointInstance } from '@gitlab/ui/dist/utils'; -import { n__, s__ } from '~/locale'; +import { n__ } from '~/locale'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; import { numberToHumanSize } from '~/lib/utils/number_utils'; import timeagoMixin from '~/vue_shared/mixins/timeago'; @@ -35,6 +37,14 @@ import { DELETE_TAG_ERROR_MESSAGE, DELETE_TAGS_SUCCESS_MESSAGE, DELETE_TAGS_ERROR_MESSAGE, + REMOVE_TAG_CONFIRMATION_TEXT, + REMOVE_TAGS_CONFIRMATION_TEXT, + DETAILS_PAGE_TITLE, + REMOVE_TAGS_BUTTON_TITLE, + REMOVE_TAG_BUTTON_TITLE, + EMPTY_IMAGE_REPOSITORY_TITLE, + EMPTY_IMAGE_REPOSITORY_MESSAGE, + ADMIN_GARBAGE_COLLECTION_TIP, } from '../constants'; export default { @@ -49,6 +59,8 @@ export default { GlSkeletonLoader, GlSprintf, GlEmptyState, + GlAlert, + GlLink, }, directives: { GlTooltip: GlTooltipDirective, @@ -60,6 +72,19 @@ export default { width: 1000, height: 40, }, + i18n: { + DETAILS_PAGE_TITLE, + REMOVE_TAGS_BUTTON_TITLE, + REMOVE_TAG_BUTTON_TITLE, + EMPTY_IMAGE_REPOSITORY_TITLE, + EMPTY_IMAGE_REPOSITORY_MESSAGE, + }, + alertMessages: { + success_tag: DELETE_TAG_SUCCESS_MESSAGE, + danger_tag: DELETE_TAG_ERROR_MESSAGE, + success_tags: DELETE_TAGS_SUCCESS_MESSAGE, + danger_tags: DELETE_TAGS_ERROR_MESSAGE, + }, data() { return { selectedItems: [], @@ -67,6 +92,7 @@ export default { selectAllChecked: false, modalDescription: null, isDesktop: true, + deleteAlertType: false, }; }, computed: { @@ -110,20 +136,40 @@ export default { this.requestTagsList({ pagination: { page }, params: this.$route.params.id }); }, }, + deleteAlertConfig() { + const config = { + title: '', + message: '', + type: 'success', + }; + if (this.deleteAlertType) { + [config.type] = this.deleteAlertType.split('_'); + + const defaultMessage = this.$options.alertMessages[this.deleteAlertType]; + + if (this.config.isAdmin && config.type === 'success') { + config.title = defaultMessage; + config.message = ADMIN_GARBAGE_COLLECTION_TIP; + } else { + config.message = defaultMessage; + } + } + return config; + }, }, methods: { ...mapActions(['requestTagsList', 'requestDeleteTag', 'requestDeleteTags']), setModalDescription(itemIndex = -1) { if (itemIndex === -1) { this.modalDescription = { - message: s__(`ContainerRegistry|You are about to remove %{item} tags. Are you sure?`), + message: REMOVE_TAGS_CONFIRMATION_TEXT, item: this.itemsToBeDeleted.length, }; } else { const { path } = this.tags[itemIndex]; this.modalDescription = { - message: s__(`ContainerRegistry|You are about to remove %{item}. Are you sure?`), + message: REMOVE_TAG_CONFIRMATION_TEXT, item: path, }; } @@ -179,19 +225,17 @@ export default { this.track('click_button'); this.$refs.deleteModal.show(); }, - handleSingleDelete(itemToDelete) { + handleSingleDelete(index) { + const itemToDelete = this.tags[index]; this.itemsToBeDeleted = []; + this.selectedItems = this.selectedItems.filter(i => i !== index); return this.requestDeleteTag({ tag: itemToDelete, params: this.$route.params.id }) - .then(() => - this.$toast.show(DELETE_TAG_SUCCESS_MESSAGE, { - type: 'success', - }), - ) - .catch(() => - this.$toast.show(DELETE_TAG_ERROR_MESSAGE, { - type: 'error', - }), - ); + .then(() => { + this.deleteAlertType = 'success_tag'; + }) + .catch(() => { + this.deleteAlertType = 'danger_tag'; + }); }, handleMultipleDelete() { const { itemsToBeDeleted } = this; @@ -202,24 +246,19 @@ export default { ids: itemsToBeDeleted.map(x => this.tags[x].name), params: this.$route.params.id, }) - .then(() => - this.$toast.show(DELETE_TAGS_SUCCESS_MESSAGE, { - type: 'success', - }), - ) - .catch(() => - this.$toast.show(DELETE_TAGS_ERROR_MESSAGE, { - type: 'error', - }), - ); + .then(() => { + this.deleteAlertType = 'success_tags'; + }) + .catch(() => { + this.deleteAlertType = 'danger_tags'; + }); }, onDeletionConfirmed() { this.track('confirm_delete'); if (this.isMultiDelete) { this.handleMultipleDelete(); } else { - const index = this.itemsToBeDeleted[0]; - this.handleSingleDelete(this.tags[index]); + this.handleSingleDelete(this.itemsToBeDeleted[0]); } }, handleResize() { @@ -231,9 +270,24 @@ export default { <template> <div v-gl-resize-observer="handleResize" class="my-3 w-100 slide-enter-to-element"> + <gl-alert + v-if="deleteAlertType" + :variant="deleteAlertConfig.type" + :title="deleteAlertConfig.title" + class="my-2" + @dismiss="deleteAlertType = null" + > + <gl-sprintf :message="deleteAlertConfig.message"> + <template #docLink="{content}"> + <gl-link :href="config.garbageCollectionHelpPagePath" target="_blank"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + </gl-alert> <div class="d-flex my-3 align-items-center"> <h4> - <gl-sprintf :message="s__('ContainerRegistry|%{imageName} tags')"> + <gl-sprintf :message="$options.i18n.DETAILS_PAGE_TITLE"> <template #imageName> {{ imageName }} </template> @@ -256,8 +310,8 @@ export default { :disabled="!selectedItems || selectedItems.length === 0" class="float-right" variant="danger" - :title="s__('ContainerRegistry|Remove selected tags')" - :aria-label="s__('ContainerRegistry|Remove selected tags')" + :title="$options.i18n.REMOVE_TAGS_BUTTON_TITLE" + :aria-label="$options.i18n.REMOVE_TAGS_BUTTON_TITLE" @click="deleteMultipleItems()" > <gl-icon name="remove" /> @@ -306,8 +360,8 @@ export default { <template #cell(actions)="{index, item}"> <gl-deprecated-button ref="singleDeleteButton" - :title="s__('ContainerRegistry|Remove tag')" - :aria-label="s__('ContainerRegistry|Remove tag')" + :title="$options.i18n.REMOVE_TAG_BUTTON_TITLE" + :aria-label="$options.i18n.REMOVE_TAG_BUTTON_TITLE" :disabled="!item.destroy_path" variant="danger" class="js-delete-registry float-right btn-inverted btn-border-color btn-icon" @@ -337,15 +391,9 @@ export default { </template> <gl-empty-state v-else - :title="s__('ContainerRegistry|This image has no active tags')" + :title="$options.i18n.EMPTY_IMAGE_REPOSITORY_TITLE" :svg-path="config.noContainersImage" - :description=" - s__( - `ContainerRegistry|The last tag related to this image was recently removed. - This empty image and any associated data will be automatically removed as part of the regular Garbage Collection process. - If you have any questions, contact your administrator.`, - ) - " + :description="$options.i18n.EMPTY_IMAGE_REPOSITORY_MESSAGE" class="mx-auto my-0" /> </template> diff --git a/app/assets/javascripts/registry/explorer/pages/index.vue b/app/assets/javascripts/registry/explorer/pages/index.vue index 95d83c82987..709a163d56d 100644 --- a/app/assets/javascripts/registry/explorer/pages/index.vue +++ b/app/assets/javascripts/registry/explorer/pages/index.vue @@ -1,46 +1,9 @@ <script> -import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui'; -import { mapState, mapActions, mapGetters } from 'vuex'; -import { s__ } from '~/locale'; - -export default { - components: { - GlAlert, - GlSprintf, - GlLink, - }, - i18n: { - garbageCollectionTipText: s__( - 'ContainerRegistry|This Registry contains deleted image tag data. Remember to run %{docLinkStart}garbage collection%{docLinkEnd} to remove the stale data from storage.', - ), - }, - computed: { - ...mapState(['config']), - ...mapGetters(['showGarbageCollection']), - }, - methods: { - ...mapActions(['setShowGarbageCollectionTip']), - }, -}; +export default {}; </script> <template> <div> - <gl-alert - v-if="showGarbageCollection" - variant="tip" - class="my-2" - @dismiss="setShowGarbageCollectionTip(false)" - > - <gl-sprintf :message="$options.i18n.garbageCollectionTipText"> - <template #docLink="{content}"> - <gl-link :href="config.garbageCollectionHelpPagePath" target="_blank"> - {{ content }} - </gl-link> - </template> - </gl-sprintf> - </gl-alert> - <transition name="slide"> <router-view ref="router-view" /> </transition> diff --git a/app/assets/javascripts/registry/explorer/stores/actions.js b/app/assets/javascripts/registry/explorer/stores/actions.js index b4f66dbbcd6..6e3cf3f0c80 100644 --- a/app/assets/javascripts/registry/explorer/stores/actions.js +++ b/app/assets/javascripts/registry/explorer/stores/actions.js @@ -66,7 +66,7 @@ export const requestDeleteTag = ({ commit, dispatch, state }, { tag, params }) = dispatch('setShowGarbageCollectionTip', true); return dispatch('requestTagsList', { pagination: state.tagsPagination, params }); }) - .catch(() => { + .finally(() => { commit(types.SET_MAIN_LOADING, false); }); }; @@ -83,7 +83,7 @@ export const requestDeleteTags = ({ commit, dispatch, state }, { ids, params }) dispatch('setShowGarbageCollectionTip', true); return dispatch('requestTagsList', { pagination: state.tagsPagination, params }); }) - .catch(() => { + .finally(() => { commit(types.SET_MAIN_LOADING, false); }); }; diff --git a/app/models/group.rb b/app/models/group.rb index d61705ca605..fd0f9cc223c 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -15,7 +15,6 @@ class Group < Namespace include WithUploads include Gitlab::Utils::StrongMemoize include GroupAPICompatibility - include HasWiki ACCESS_REQUEST_APPROVERS_TO_BE_NOTIFIED_LIMIT = 10 diff --git a/app/models/group_wiki.rb b/app/models/group_wiki.rb deleted file mode 100644 index 703ef80c121..00000000000 --- a/app/models/group_wiki.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -class GroupWiki < Wiki - alias_method :group, :container - - override :storage - def storage - @storage ||= Storage::Hashed.new(container, prefix: Storage::Hashed::GROUP_REPOSITORY_PATH_PREFIX) - end - - override :repository_storage - def repository_storage - # TODO: Add table to track storage - # https://gitlab.com/gitlab-org/gitlab/-/issues/207865 - 'default' - end - - override :hashed_storage? - def hashed_storage? - true - end - - override :disk_path - def disk_path(*args, &block) - storage.disk_path + '.wiki' - end -end diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index e8987265a93..5f596ea7920 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -86,9 +86,9 @@ .content-block.emoji-block.emoji-block-sticky .row - .col-md-12.col-lg-6.js-noteable-awards + .col-md-12.col-lg-4.js-noteable-awards = render 'award_emoji/awards_block', awardable: @issue, inline: true - .col-md-12.col-lg-6.new-branch-col + .col-md-12.col-lg-8.new-branch-col #js-vue-sort-issue-discussions #js-vue-discussion-filter{ data: { default_filter: current_user&.notes_filter_for(@issue), notes_filters: UserPreference.notes_filters.to_json } } = render 'new_branch' if show_new_branch_button? diff --git a/app/views/projects/static_site_editor/show.html.haml b/app/views/projects/static_site_editor/show.html.haml index 88c5378fc35..8d2649be588 100644 --- a/app/views/projects/static_site_editor/show.html.haml +++ b/app/views/projects/static_site_editor/show.html.haml @@ -1,4 +1 @@ --# TODO: Remove after base URL is included in the model !30735 -- data = @config.payload.merge({ base_url: namespace_project_show_sse_path }) - -#static-site-editor{ data: data } +#static-site-editor{ data: @config.payload } diff --git a/changelogs/unreleased/214477-reactor-toast-to-alert-in-image-repository-details-page.yml b/changelogs/unreleased/214477-reactor-toast-to-alert-in-image-repository-details-page.yml new file mode 100644 index 00000000000..a87cb780b8c --- /dev/null +++ b/changelogs/unreleased/214477-reactor-toast-to-alert-in-image-repository-details-page.yml @@ -0,0 +1,5 @@ +--- +title: Use alerts instead of toasts in Image Repository details +merge_request: 29685 +author: +type: changed diff --git a/changelogs/unreleased/fix-issue-details-row-emoji-block.yml b/changelogs/unreleased/fix-issue-details-row-emoji-block.yml new file mode 100644 index 00000000000..15d9025e3b4 --- /dev/null +++ b/changelogs/unreleased/fix-issue-details-row-emoji-block.yml @@ -0,0 +1,5 @@ +--- +title: Fix layout in issue view, on large screen some buttons were misaligned +merge_request: 30947 +author: Michele (macno) Azzolari +type: fixed diff --git a/changelogs/unreleased/sh-revert-codeowners-check.yml b/changelogs/unreleased/sh-revert-codeowners-check.yml new file mode 100644 index 00000000000..04181d0f678 --- /dev/null +++ b/changelogs/unreleased/sh-revert-codeowners-check.yml @@ -0,0 +1,5 @@ +--- +title: Revert CODEOWNERS validation of Web requests in diff check +merge_request: 31087 +author: +type: fixed diff --git a/doc/development/testing_guide/review_apps.md b/doc/development/testing_guide/review_apps.md index 606f7c4d6b5..dd2008a2003 100644 --- a/doc/development/testing_guide/review_apps.md +++ b/doc/development/testing_guide/review_apps.md @@ -153,7 +153,13 @@ used by the `review-deploy` and `review-stop` jobs. ### Get access to the GCP Review Apps cluster You need to [open an access request (internal link)](https://gitlab.com/gitlab-com/access-requests/issues/new) -for the `gcp-review-apps-sg` GCP group. +for the `gcp-review-apps-sg` GCP group. In order to join a group, you must specify the desired GCP role in your access request. +The role is what will grant you specific permissions in order to engage with Review App containers. + +Here are some permissions you may want to have, and the roles that grant them: + +- `container.pods.getLogs` - Required to [retrieve pod logs](#dig-into-a-pods-logs). Granted by [Viewer (`roles/viewer`)](https://cloud.google.com/iam/docs/understanding-roles#kubernetes-engine-roles). +- `container.pods.exec` - Required to [run a Rails console](#run-a-rails-console). Granted by [Kubernetes Engine Developer (`roles/container.developer`)](https://cloud.google.com/iam/docs/understanding-roles#kubernetes-engine-roles). ### Log into my Review App @@ -175,7 +181,7 @@ secure note named `gitlab-{ce,ee} Review App's root password`. ### Run a Rails console -1. Make sure you [have access to the cluster](#get-access-to-the-gcp-review-apps-cluster) first. +1. Make sure you [have access to the cluster](#get-access-to-the-gcp-review-apps-cluster) and the `container.pods.exec` permission first. 1. [Filter Workloads by your Review App slug](https://console.cloud.google.com/kubernetes/workload?project=gitlab-review-apps), e.g. `review-qa-raise-e-12chm0`. 1. Find and open the `task-runner` Deployment, e.g. `review-qa-raise-e-12chm0-task-runner`. @@ -191,7 +197,7 @@ secure note named `gitlab-{ce,ee} Review App's root password`. ### Dig into a Pod's logs -1. Make sure you [have access to the cluster](#get-access-to-the-gcp-review-apps-cluster) first. +1. Make sure you [have access to the cluster](#get-access-to-the-gcp-review-apps-cluster) and the `container.pods.getLogs` permission first. 1. [Filter Workloads by your Review App slug](https://console.cloud.google.com/kubernetes/workload?project=gitlab-review-apps), e.g. `review-qa-raise-e-12chm0`. 1. Find and open the `migrations` Deployment, e.g. diff --git a/lib/gitlab/metrics/samplers/ruby_sampler.rb b/lib/gitlab/metrics/samplers/ruby_sampler.rb index c38769f39a9..5cd2a86a106 100644 --- a/lib/gitlab/metrics/samplers/ruby_sampler.rb +++ b/lib/gitlab/metrics/samplers/ruby_sampler.rb @@ -34,14 +34,16 @@ module Gitlab def init_metrics metrics = { - file_descriptors: ::Gitlab::Metrics.gauge(with_prefix(:file, :descriptors), 'File descriptors used', labels), - memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:memory, :bytes), 'Memory used', labels), - process_cpu_seconds_total: ::Gitlab::Metrics.gauge(with_prefix(:process, :cpu_seconds_total), 'Process CPU seconds total'), - process_max_fds: ::Gitlab::Metrics.gauge(with_prefix(:process, :max_fds), 'Process max fds'), - process_resident_memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:process, :resident_memory_bytes), 'Memory used', labels), - process_start_time_seconds: ::Gitlab::Metrics.gauge(with_prefix(:process, :start_time_seconds), 'Process start time seconds'), - sampler_duration: ::Gitlab::Metrics.counter(with_prefix(:sampler, :duration_seconds_total), 'Sampler time', labels), - gc_duration_seconds: ::Gitlab::Metrics.histogram(with_prefix(:gc, :duration_seconds), 'GC time', labels, GC_REPORT_BUCKETS) + file_descriptors: ::Gitlab::Metrics.gauge(with_prefix(:file, :descriptors), 'File descriptors used', labels), + memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:memory, :bytes), 'Memory used (RSS)', labels), + process_cpu_seconds_total: ::Gitlab::Metrics.gauge(with_prefix(:process, :cpu_seconds_total), 'Process CPU seconds total'), + process_max_fds: ::Gitlab::Metrics.gauge(with_prefix(:process, :max_fds), 'Process max fds'), + process_resident_memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:process, :resident_memory_bytes), 'Memory used (RSS)', labels), + process_unique_memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:process, :unique_memory_bytes), 'Memory used (USS)', labels), + process_proportional_memory_bytes: ::Gitlab::Metrics.gauge(with_prefix(:process, :proportional_memory_bytes), 'Memory used (PSS)', labels), + process_start_time_seconds: ::Gitlab::Metrics.gauge(with_prefix(:process, :start_time_seconds), 'Process start time seconds'), + sampler_duration: ::Gitlab::Metrics.counter(with_prefix(:sampler, :duration_seconds_total), 'Sampler time', labels), + gc_duration_seconds: ::Gitlab::Metrics.histogram(with_prefix(:gc, :duration_seconds), 'GC time', labels, GC_REPORT_BUCKETS) } GC.stat.keys.each do |key| @@ -85,10 +87,15 @@ module Gitlab end def set_memory_usage_metrics - memory_usage = System.memory_usage - - metrics[:memory_bytes].set(labels, memory_usage) - metrics[:process_resident_memory_bytes].set(labels, memory_usage) + memory_rss = System.memory_usage + metrics[:memory_bytes].set(labels, memory_rss) + metrics[:process_resident_memory_bytes].set(labels, memory_rss) + + if Feature.enabled?(:collect_memory_uss_pss) + memory_uss_pss = System.memory_usage_uss_pss + metrics[:process_unique_memory_bytes].set(labels, memory_uss_pss[:uss]) + metrics[:process_proportional_memory_bytes].set(labels, memory_uss_pss[:pss]) + end end end end diff --git a/lib/gitlab/metrics/system.rb b/lib/gitlab/metrics/system.rb index 2a61b3de405..d01b6bc5b50 100644 --- a/lib/gitlab/metrics/system.rb +++ b/lib/gitlab/metrics/system.rb @@ -7,47 +7,37 @@ module Gitlab # This module relies on the /proc filesystem being available. If /proc is # not available the methods of this module will be stubbed. module System - if File.exist?('/proc') - # Returns the current process' memory usage in bytes. - def self.memory_usage - mem = 0 - match = File.read('/proc/self/status').match(/VmRSS:\s+(\d+)/) - - if match && match[1] - mem = match[1].to_f * 1024 - end - - mem - end - - def self.file_descriptor_count - Dir.glob('/proc/self/fd/*').length - end - - def self.max_open_file_descriptors - match = File.read('/proc/self/limits').match(/Max open files\s*(\d+)/) - - return unless match && match[1] + PROC_STATUS_PATH = '/proc/self/status' + PROC_SMAPS_ROLLUP_PATH = '/proc/self/smaps_rollup' + PROC_LIMITS_PATH = '/proc/self/limits' + PROC_FD_GLOB = '/proc/self/fd/*' + + PRIVATE_PAGES_PATTERN = /^(Private_Clean|Private_Dirty|Private_Hugetlb):\s+(?<value>\d+)/.freeze + PSS_PATTERN = /^Pss:\s+(?<value>\d+)/.freeze + RSS_PATTERN = /VmRSS:\s+(?<value>\d+)/.freeze + MAX_OPEN_FILES_PATTERN = /Max open files\s*(?<value>\d+)/.freeze + + # Returns the current process' RSS (resident set size) in bytes. + def self.memory_usage + sum_matches(PROC_STATUS_PATH, rss: RSS_PATTERN)[:rss].kilobytes + end - match[1].to_i - end - else - def self.memory_usage - 0.0 - end + # Returns the current process' USS/PSS (unique/proportional set size) in bytes. + def self.memory_usage_uss_pss + sum_matches(PROC_SMAPS_ROLLUP_PATH, uss: PRIVATE_PAGES_PATTERN, pss: PSS_PATTERN) + .transform_values(&:kilobytes) + end - def self.file_descriptor_count - 0 - end + def self.file_descriptor_count + Dir.glob(PROC_FD_GLOB).length + end - def self.max_open_file_descriptors - 0 - end + def self.max_open_file_descriptors + sum_matches(PROC_LIMITS_PATH, max_fds: MAX_OPEN_FILES_PATTERN)[:max_fds] end def self.cpu_time - Process - .clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :float_second) + Process.clock_gettime(Process::CLOCK_PROCESS_CPUTIME_ID, :float_second) end # Returns the current real time in a given precision. @@ -78,6 +68,27 @@ module Gitlab end_time - start_time end + + # Given a path to a file in /proc and a hash of (metric, pattern) pairs, + # sums up all values found for those patterns under the respective metric. + def self.sum_matches(proc_file, **patterns) + results = patterns.transform_values { 0 } + + begin + File.foreach(proc_file) do |line| + patterns.each do |metric, pattern| + match = line.match(pattern) + value = match&.named_captures&.fetch('value', 0) + results[metric] += value.to_i + end + end + rescue Errno::ENOENT + # This means the procfile we're reading from did not exist; + # this is safe to ignore, since we initialize each metric to 0 + end + + results + end end end end diff --git a/lib/gitlab/static_site_editor/config.rb b/lib/gitlab/static_site_editor/config.rb index 1862d949817..c931cdecbeb 100644 --- a/lib/gitlab/static_site_editor/config.rb +++ b/lib/gitlab/static_site_editor/config.rb @@ -22,7 +22,8 @@ module Gitlab project: project.path, namespace: project.namespace.path, return_url: return_url, - is_supported_content: supported_content?.to_s + is_supported_content: supported_content?.to_s, + base_url: Gitlab::Routing.url_helpers.project_show_sse_path(project, full_path) } end @@ -47,6 +48,10 @@ module Gitlab def file_exists? commit_id.present? && repository.blob_at(commit_id, file_path).present? end + + def full_path + "#{ref}/#{file_path}" + end end end end diff --git a/lib/gitlab/url_builder.rb b/lib/gitlab/url_builder.rb index 80e31b06ba2..d292b36b311 100644 --- a/lib/gitlab/url_builder.rb +++ b/lib/gitlab/url_builder.rb @@ -72,13 +72,8 @@ module Gitlab end def wiki_url(object, **options) - case object.container - when Project + if object.container.is_a?(Project) instance.project_wiki_url(object.container, Wiki::HOMEPAGE, **options) - when Group - # TODO: Use the new route for group wikis once we add it. - # https://gitlab.com/gitlab-org/gitlab/-/issues/211360 - instance.group_canonical_url(object.container, **options) + "/-/wikis/#{Wiki::HOMEPAGE}" else raise NotImplementedError.new("No URL builder defined for #{object.inspect}") end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index f42f139b30d..0ae16d93a6e 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -5713,6 +5713,9 @@ msgstr "" msgid "ContainerRegistry|Regular expressions such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}" msgstr "" +msgid "ContainerRegistry|Remember to run %{docLinkStart}garbage collection%{docLinkEnd} to remove the stale data from storage." +msgstr "" + msgid "ContainerRegistry|Remove repository" msgstr "" @@ -5727,19 +5730,19 @@ msgstr[1] "" msgid "ContainerRegistry|Retention policy has been Enabled" msgstr "" -msgid "ContainerRegistry|Something went wrong while deleting the tag." +msgid "ContainerRegistry|Something went wrong while fetching the expiration policy." msgstr "" -msgid "ContainerRegistry|Something went wrong while deleting the tags." +msgid "ContainerRegistry|Something went wrong while fetching the repository list." msgstr "" -msgid "ContainerRegistry|Something went wrong while fetching the expiration policy." +msgid "ContainerRegistry|Something went wrong while fetching the tags list." msgstr "" -msgid "ContainerRegistry|Something went wrong while fetching the repository list." +msgid "ContainerRegistry|Something went wrong while marking the tag for deletion." msgstr "" -msgid "ContainerRegistry|Something went wrong while fetching the tags list." +msgid "ContainerRegistry|Something went wrong while marking the tags for deletion." msgstr "" msgid "ContainerRegistry|Something went wrong while scheduling %{title} for deletion. Please try again." @@ -5751,16 +5754,16 @@ msgstr "" msgid "ContainerRegistry|Tag" msgstr "" -msgid "ContainerRegistry|Tag deleted successfully" -msgstr "" - msgid "ContainerRegistry|Tag expiration policy" msgstr "" msgid "ContainerRegistry|Tag expiration policy is designed to:" msgstr "" -msgid "ContainerRegistry|Tags deleted successfully" +msgid "ContainerRegistry|Tag successfully marked for deletion." +msgstr "" + +msgid "ContainerRegistry|Tags successfully marked for deletion." msgstr "" msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}" @@ -5793,9 +5796,6 @@ msgstr "" msgid "ContainerRegistry|There was an error during the deletion of this image repository, please try again." msgstr "" -msgid "ContainerRegistry|This Registry contains deleted image tag data. Remember to run %{docLinkStart}garbage collection%{docLinkEnd} to remove the stale data from storage." -msgstr "" - msgid "ContainerRegistry|This image has no active tags" msgstr "" diff --git a/package.json b/package.json index c750481cd84..b5b772601eb 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "@babel/plugin-syntax-import-meta": "^7.8.3", "@babel/preset-env": "^7.8.4", "@gitlab/at.js": "1.5.5", - "@gitlab/svgs": "1.123.0", + "@gitlab/svgs": "1.125.0", "@gitlab/ui": "13.9.0", "@gitlab/visual-review-tools": "1.6.1", "@rails/actioncable": "^6.0.2-2", diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb index 6d292d529ab..4b6c1756d1e 100644 --- a/spec/factories/groups.rb +++ b/spec/factories/groups.rb @@ -51,11 +51,5 @@ FactoryBot.define do trait :owner_subgroup_creation_only do subgroup_creation_level { ::Gitlab::Access::OWNER_SUBGROUP_ACCESS} end - - trait :wiki_repo do - after(:create) do |group| - raise 'Failed to create wiki repository!' unless group.create_wiki - end - end end end diff --git a/spec/factories/wikis.rb b/spec/factories/wikis.rb index b591b0f169d..96578fdcee6 100644 --- a/spec/factories/wikis.rb +++ b/spec/factories/wikis.rb @@ -17,9 +17,5 @@ FactoryBot.define do container { project } end - - factory :group_wiki do - container { association(:group, :wiki_repo) } - end end end diff --git a/spec/features/groups/members/leave_group_spec.rb b/spec/features/groups/members/leave_group_spec.rb index df15f995be4..5c7c83aea6d 100644 --- a/spec/features/groups/members/leave_group_spec.rb +++ b/spec/features/groups/members/leave_group_spec.rb @@ -31,8 +31,6 @@ describe 'Groups > Members > Leave group' do page.accept_confirm - expect(find('.flash-notice')).to have_content "You left the \"#{group.full_name}\" group" - expect(page).to have_content left_group_message(group) expect(current_path).to eq(dashboard_groups_path) expect(group.users).not_to include(user) end diff --git a/spec/frontend/registry/explorer/pages/details_spec.js b/spec/frontend/registry/explorer/pages/details_spec.js index 15aa5008413..d6741aa3141 100644 --- a/spec/frontend/registry/explorer/pages/details_spec.js +++ b/spec/frontend/registry/explorer/pages/details_spec.js @@ -1,15 +1,16 @@ import { mount } from '@vue/test-utils'; -import { GlTable, GlPagination, GlSkeletonLoader } from '@gitlab/ui'; +import { GlTable, GlPagination, GlSkeletonLoader, GlAlert, GlLink } from '@gitlab/ui'; import Tracking from '~/tracking'; import stubChildren from 'helpers/stub_children'; import component from '~/registry/explorer/pages/details.vue'; -import store from '~/registry/explorer/stores/'; -import { SET_MAIN_LOADING } from '~/registry/explorer/stores/mutation_types/'; +import { createStore } from '~/registry/explorer/stores/'; +import { SET_MAIN_LOADING, SET_INITIAL_STATE } from '~/registry/explorer/stores/mutation_types/'; import { DELETE_TAG_SUCCESS_MESSAGE, DELETE_TAG_ERROR_MESSAGE, DELETE_TAGS_SUCCESS_MESSAGE, DELETE_TAGS_ERROR_MESSAGE, + ADMIN_GARBAGE_COLLECTION_TIP, } from '~/registry/explorer/constants'; import { tagsListResponse } from '../mock_data'; import { GlModal } from '../stubs'; @@ -18,6 +19,7 @@ import { $toast } from '../../shared/mocks'; describe('Details Page', () => { let wrapper; let dispatchSpy; + let store; const findDeleteModal = () => wrapper.find(GlModal); const findPagination = () => wrapper.find(GlPagination); @@ -30,6 +32,7 @@ describe('Details Page', () => { const findAllCheckboxes = () => wrapper.findAll('.js-row-checkbox'); const findCheckedCheckboxes = () => findAllCheckboxes().filter(c => c.attributes('checked')); const findFirsTagColumn = () => wrapper.find('.js-tag-column'); + const findAlert = () => wrapper.find(GlAlert); const routeId = window.btoa(JSON.stringify({ name: 'foo', tags_path: 'bar' })); @@ -55,6 +58,7 @@ describe('Details Page', () => { }; beforeEach(() => { + store = createStore(); dispatchSpy = jest.spyOn(store, 'dispatch'); store.dispatch('receiveTagsListSuccess', tagsListResponse); jest.spyOn(Tracking, 'event'); @@ -62,6 +66,7 @@ describe('Details Page', () => { afterEach(() => { wrapper.destroy(); + wrapper = null; }); describe('when isLoading is true', () => { @@ -328,25 +333,9 @@ describe('Details Page', () => { }); // itemsToBeDeleted is not represented in the DOM, is used as parking variable between selected and deleted items expect(wrapper.vm.itemsToBeDeleted).toEqual([]); + expect(wrapper.vm.selectedItems).toEqual([]); expect(findCheckedCheckboxes()).toHaveLength(0); }); - - it('show success toast on successful delete', () => { - return wrapper.vm.handleSingleDelete(0).then(() => { - expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAG_SUCCESS_MESSAGE, { - type: 'success', - }); - }); - }); - - it('show error toast on erred delete', () => { - dispatchSpy.mockRejectedValue(); - return wrapper.vm.handleSingleDelete(0).then(() => { - expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAG_ERROR_MESSAGE, { - type: 'error', - }); - }); - }); }); describe('when multiple elements are selected', () => { @@ -365,23 +354,6 @@ describe('Details Page', () => { expect(wrapper.vm.itemsToBeDeleted).toEqual([]); expect(findCheckedCheckboxes()).toHaveLength(0); }); - - it('show success toast on successful delete', () => { - return wrapper.vm.handleMultipleDelete(0).then(() => { - expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAGS_SUCCESS_MESSAGE, { - type: 'success', - }); - }); - }); - - it('show error toast on erred delete', () => { - dispatchSpy.mockRejectedValue(); - return wrapper.vm.handleMultipleDelete(0).then(() => { - expect(wrapper.vm.$toast.show).toHaveBeenCalledWith(DELETE_TAGS_ERROR_MESSAGE, { - type: 'error', - }); - }); - }); }); }); @@ -395,4 +367,108 @@ describe('Details Page', () => { }); }); }); + + describe('Delete alert', () => { + const config = { + garbageCollectionHelpPagePath: 'foo', + }; + + describe('when the user is an admin', () => { + beforeEach(() => { + store.commit(SET_INITIAL_STATE, { ...config, isAdmin: true }); + }); + + afterEach(() => { + store.commit(SET_INITIAL_STATE, config); + }); + + describe.each` + deleteType | successTitle | errorTitle + ${'handleSingleDelete'} | ${DELETE_TAG_SUCCESS_MESSAGE} | ${DELETE_TAG_ERROR_MESSAGE} + ${'handleMultipleDelete'} | ${DELETE_TAGS_SUCCESS_MESSAGE} | ${DELETE_TAGS_ERROR_MESSAGE} + `('behaves correctly on $deleteType', ({ deleteType, successTitle, errorTitle }) => { + describe('when delete is successful', () => { + beforeEach(() => { + dispatchSpy.mockResolvedValue(); + mountComponent(); + return wrapper.vm[deleteType]('foo'); + }); + + it('alert exists', () => { + expect(findAlert().exists()).toBe(true); + }); + + it('alert body contains admin tip', () => { + expect( + findAlert() + .text() + .replace(/\s\s+/gm, ' '), + ).toBe(ADMIN_GARBAGE_COLLECTION_TIP.replace(/%{\w+}/gm, '')); + }); + + it('alert body contains link', () => { + const alertLink = findAlert().find(GlLink); + expect(alertLink.exists()).toBe(true); + expect(alertLink.attributes('href')).toBe(config.garbageCollectionHelpPagePath); + }); + + it('alert title is appropriate', () => { + expect(findAlert().attributes('title')).toBe(successTitle); + }); + }); + + describe('when delete is not successful', () => { + beforeEach(() => { + mountComponent(); + dispatchSpy.mockRejectedValue(); + return wrapper.vm[deleteType]('foo'); + }); + + it('alert exist and text is appropriate', () => { + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe(errorTitle); + }); + }); + }); + }); + + describe.each` + deleteType | successTitle | errorTitle + ${'handleSingleDelete'} | ${DELETE_TAG_SUCCESS_MESSAGE} | ${DELETE_TAG_ERROR_MESSAGE} + ${'handleMultipleDelete'} | ${DELETE_TAGS_SUCCESS_MESSAGE} | ${DELETE_TAGS_ERROR_MESSAGE} + `( + 'when the user is not an admin alert behaves correctly on $deleteType', + ({ deleteType, successTitle, errorTitle }) => { + beforeEach(() => { + store.commit('SET_INITIAL_STATE', { ...config }); + }); + + describe('when delete is successful', () => { + beforeEach(() => { + dispatchSpy.mockResolvedValue(); + mountComponent(); + return wrapper.vm[deleteType]('foo'); + }); + + it('alert exist and text is appropriate', () => { + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe(successTitle); + }); + }); + + describe('when delete is not successful', () => { + beforeEach(() => { + mountComponent(); + dispatchSpy.mockRejectedValue(); + return wrapper.vm[deleteType]('foo'); + }); + + it('alert exist and text is appropriate', () => { + expect(findAlert().exists()).toBe(true); + expect(findAlert().text()).toBe(errorTitle); + }); + }); + }, + ); + }); }); diff --git a/spec/frontend/registry/explorer/pages/index_spec.js b/spec/frontend/registry/explorer/pages/index_spec.js index f52e7d67866..b558727ed5e 100644 --- a/spec/frontend/registry/explorer/pages/index_spec.js +++ b/spec/frontend/registry/explorer/pages/index_spec.js @@ -1,62 +1,26 @@ import { shallowMount } from '@vue/test-utils'; -import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui'; import component from '~/registry/explorer/pages/index.vue'; import store from '~/registry/explorer/stores/'; describe('List Page', () => { let wrapper; - let dispatchSpy; const findRouterView = () => wrapper.find({ ref: 'router-view' }); - const findAlert = () => wrapper.find(GlAlert); - const findLink = () => wrapper.find(GlLink); const mountComponent = () => { wrapper = shallowMount(component, { store, stubs: { RouterView: true, - GlSprintf, }, }); }; beforeEach(() => { - dispatchSpy = jest.spyOn(store, 'dispatch'); mountComponent(); }); it('has a router view', () => { expect(findRouterView().exists()).toBe(true); }); - - describe('garbageCollectionTip alert', () => { - beforeEach(() => { - store.dispatch('setInitialState', { isAdmin: true, garbageCollectionHelpPagePath: 'foo' }); - store.dispatch('setShowGarbageCollectionTip', true); - }); - - afterEach(() => { - store.dispatch('setInitialState', {}); - store.dispatch('setShowGarbageCollectionTip', false); - }); - - it('is visible when the user is an admin and the user performed a delete action', () => { - expect(findAlert().exists()).toBe(true); - }); - - it('on dismiss disappears ', () => { - findAlert().vm.$emit('dismiss'); - expect(dispatchSpy).toHaveBeenCalledWith('setShowGarbageCollectionTip', false); - return wrapper.vm.$nextTick().then(() => { - expect(findAlert().exists()).toBe(false); - }); - }); - - it('contains a link to the docs', () => { - const link = findLink(); - expect(link.exists()).toBe(true); - expect(link.attributes('href')).toBe(store.state.config.garbageCollectionHelpPagePath); - }); - }); }); diff --git a/spec/frontend/registry/explorer/stores/actions_spec.js b/spec/frontend/registry/explorer/stores/actions_spec.js index 58f61a0e8c2..15f9db90910 100644 --- a/spec/frontend/registry/explorer/stores/actions_spec.js +++ b/spec/frontend/registry/explorer/stores/actions_spec.js @@ -191,7 +191,10 @@ describe('Actions RegistryExplorer Store', () => { { tagsPagination: {}, }, - [{ type: types.SET_MAIN_LOADING, payload: true }], + [ + { type: types.SET_MAIN_LOADING, payload: true }, + { type: types.SET_MAIN_LOADING, payload: false }, + ], [ { type: 'setShowGarbageCollectionTip', @@ -220,8 +223,7 @@ describe('Actions RegistryExplorer Store', () => { { type: types.SET_MAIN_LOADING, payload: false }, ], [], - done, - ); + ).catch(() => done()); }); }); @@ -241,7 +243,10 @@ describe('Actions RegistryExplorer Store', () => { { tagsPagination: {}, }, - [{ type: types.SET_MAIN_LOADING, payload: true }], + [ + { type: types.SET_MAIN_LOADING, payload: true }, + { type: types.SET_MAIN_LOADING, payload: false }, + ], [ { type: 'setShowGarbageCollectionTip', @@ -273,8 +278,7 @@ describe('Actions RegistryExplorer Store', () => { { type: types.SET_MAIN_LOADING, payload: false }, ], [], - done, - ); + ).catch(() => done()); }); }); @@ -311,9 +315,7 @@ describe('Actions RegistryExplorer Store', () => { { type: types.SET_MAIN_LOADING, payload: false }, ], [], - ).catch(() => { - done(); - }); + ).catch(() => done()); }); }); }); diff --git a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb index 8c4071a7ed1..9d8ec2d9b21 100644 --- a/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb +++ b/spec/lib/gitlab/metrics/samplers/ruby_sampler_spec.rb @@ -19,20 +19,19 @@ describe Gitlab::Metrics::Samplers::RubySampler do end describe '#sample' do - it 'samples various statistics' do - expect(Gitlab::Metrics::System).to receive(:cpu_time) - expect(Gitlab::Metrics::System).to receive(:file_descriptor_count) - expect(Gitlab::Metrics::System).to receive(:memory_usage) - expect(Gitlab::Metrics::System).to receive(:max_open_file_descriptors) - expect(sampler).to receive(:sample_gc) + it 'adds a metric containing the process resident memory bytes' do + expect(Gitlab::Metrics::System).to receive(:memory_usage).and_return(9000) + + expect(sampler.metrics[:process_resident_memory_bytes]).to receive(:set).with({}, 9000) sampler.sample end - it 'adds a metric containing the process resident memory bytes' do - expect(Gitlab::Metrics::System).to receive(:memory_usage).and_return(9000) + it 'adds a metric containing the process unique and proportional memory bytes' do + expect(Gitlab::Metrics::System).to receive(:memory_usage_uss_pss).and_return(uss: 9000, pss: 10_000) - expect(sampler.metrics[:process_resident_memory_bytes]).to receive(:set).with({}, 9000) + expect(sampler.metrics[:process_unique_memory_bytes]).to receive(:set).with({}, 9000) + expect(sampler.metrics[:process_proportional_memory_bytes]).to receive(:set).with({}, 10_000) sampler.sample end diff --git a/spec/lib/gitlab/metrics/system_spec.rb b/spec/lib/gitlab/metrics/system_spec.rb index a5aa80686fd..37d26bd9d63 100644 --- a/spec/lib/gitlab/metrics/system_spec.rb +++ b/spec/lib/gitlab/metrics/system_spec.rb @@ -3,33 +3,122 @@ require 'spec_helper' describe Gitlab::Metrics::System do - if File.exist?('/proc') + context 'when /proc files exist' do + # Fixtures pulled from: + # Linux carbon 5.3.0-7648-generic #41~1586789791~19.10~9593806-Ubuntu SMP Mon Apr 13 17:50:40 UTC x86_64 x86_64 x86_64 GNU/Linux + let(:proc_status) do + # most rows omitted for brevity + <<~SNIP + Name: less + VmHWM: 2468 kB + VmRSS: 2468 kB + RssAnon: 260 kB + SNIP + end + + let(:proc_smaps_rollup) do + # full snapshot + <<~SNIP + Rss: 2564 kB + Pss: 503 kB + Pss_Anon: 312 kB + Pss_File: 191 kB + Pss_Shmem: 0 kB + Shared_Clean: 2100 kB + Shared_Dirty: 0 kB + Private_Clean: 152 kB + Private_Dirty: 312 kB + Referenced: 2564 kB + Anonymous: 312 kB + LazyFree: 0 kB + AnonHugePages: 0 kB + ShmemPmdMapped: 0 kB + Shared_Hugetlb: 0 kB + Private_Hugetlb: 0 kB + Swap: 0 kB + SwapPss: 0 kB + Locked: 0 kB + SNIP + end + + let(:proc_limits) do + # full snapshot + <<~SNIP + Limit Soft Limit Hard Limit Units + Max cpu time unlimited unlimited seconds + Max file size unlimited unlimited bytes + Max data size unlimited unlimited bytes + Max stack size 8388608 unlimited bytes + Max core file size 0 unlimited bytes + Max resident set unlimited unlimited bytes + Max processes 126519 126519 processes + Max open files 1024 1048576 files + Max locked memory 67108864 67108864 bytes + Max address space unlimited unlimited bytes + Max file locks unlimited unlimited locks + Max pending signals 126519 126519 signals + Max msgqueue size 819200 819200 bytes + Max nice priority 0 0 + Max realtime priority 0 0 + Max realtime timeout unlimited unlimited us + SNIP + end + describe '.memory_usage' do - it "returns the process' memory usage in bytes" do - expect(described_class.memory_usage).to be > 0 + it "returns the process' resident set size (RSS) in bytes" do + mock_existing_proc_file('/proc/self/status', proc_status) + + expect(described_class.memory_usage).to eq(2527232) end end describe '.file_descriptor_count' do it 'returns the amount of open file descriptors' do - expect(described_class.file_descriptor_count).to be > 0 + expect(Dir).to receive(:glob).and_return(['/some/path', '/some/other/path']) + + expect(described_class.file_descriptor_count).to eq(2) end end describe '.max_open_file_descriptors' do it 'returns the max allowed open file descriptors' do - expect(described_class.max_open_file_descriptors).to be > 0 + mock_existing_proc_file('/proc/self/limits', proc_limits) + + expect(described_class.max_open_file_descriptors).to eq(1024) + end + end + + describe '.memory_usage_uss_pss' do + it "returns the process' unique and porportional set size (USS/PSS) in bytes" do + mock_existing_proc_file('/proc/self/smaps_rollup', proc_smaps_rollup) + + # (Private_Clean (152 kB) + Private_Dirty (312 kB) + Private_Hugetlb (0 kB)) * 1024 + expect(described_class.memory_usage_uss_pss).to eq(uss: 475136, pss: 515072) end end - else + end + + context 'when /proc files do not exist' do + before do + mock_missing_proc_file + end + describe '.memory_usage' do - it 'returns 0.0' do - expect(described_class.memory_usage).to eq(0.0) + it 'returns 0' do + expect(described_class.memory_usage).to eq(0) + end + end + + describe '.memory_usage_uss_pss' do + it "returns 0 for all components" do + expect(described_class.memory_usage_uss_pss).to eq(uss: 0, pss: 0) end end describe '.file_descriptor_count' do it 'returns 0' do + expect(Dir).to receive(:glob).and_return([]) + expect(described_class.file_descriptor_count).to eq(0) end end @@ -98,4 +187,12 @@ describe Gitlab::Metrics::System do expect(described_class.thread_cpu_duration(start_time)).to be_nil end end + + def mock_existing_proc_file(path, content) + allow(File).to receive(:foreach).with(path) { |_path, &block| content.each_line(&block) } + end + + def mock_missing_proc_file + allow(File).to receive(:foreach).and_raise(Errno::ENOENT) + end end diff --git a/spec/lib/gitlab/repository_url_builder_spec.rb b/spec/lib/gitlab/repository_url_builder_spec.rb index 1eb9de48405..a5797146cc5 100644 --- a/spec/lib/gitlab/repository_url_builder_spec.rb +++ b/spec/lib/gitlab/repository_url_builder_spec.rb @@ -10,7 +10,6 @@ describe Gitlab::RepositoryUrlBuilder do :project | ->(project) { project.full_path } :project_snippet | ->(snippet) { "#{snippet.project.full_path}/snippets/#{snippet.id}" } :project_wiki | ->(wiki) { "#{wiki.container.full_path}.wiki" } - :group_wiki | ->(wiki) { "#{wiki.container.full_path}.wiki" } :personal_snippet | ->(snippet) { "snippets/#{snippet.id}" } end diff --git a/spec/lib/gitlab/static_site_editor/config_spec.rb b/spec/lib/gitlab/static_site_editor/config_spec.rb index b32af912ad9..a1db567db1a 100644 --- a/spec/lib/gitlab/static_site_editor/config_spec.rb +++ b/spec/lib/gitlab/static_site_editor/config_spec.rb @@ -5,9 +5,10 @@ require 'spec_helper' describe Gitlab::StaticSiteEditor::Config do subject(:config) { described_class.new(repository, ref, file_path, return_url) } - let(:project) { create(:project, :public, :repository, name: 'project', namespace: namespace) } - let(:namespace) { create(:namespace, name: 'namespace') } - let(:repository) { project.repository } + let_it_be(:namespace) { create(:namespace, name: 'namespace') } + let_it_be(:project) { create(:project, :public, :repository, name: 'project', namespace: namespace) } + let_it_be(:repository) { project.repository } + let(:ref) { 'master' } let(:file_path) { 'README.md' } let(:return_url) { 'http://example.com' } @@ -24,10 +25,17 @@ describe Gitlab::StaticSiteEditor::Config do project: 'project', project_id: project.id, return_url: 'http://example.com', - is_supported_content: 'true' + is_supported_content: 'true', + base_url: '/namespace/project/-/sse/master%2FREADME.md' ) end + context 'when file path is nested' do + let(:file_path) { 'lib/README.md' } + + it { is_expected.to include(base_url: '/namespace/project/-/sse/master%2Flib%2FREADME.md') } + end + context 'when branch is not master' do let(:ref) { 'my-branch' } @@ -53,7 +61,7 @@ describe Gitlab::StaticSiteEditor::Config do end context 'when repository is empty' do - let(:project) { create(:project_empty_repo) } + let(:repository) { create(:project_empty_repo).repository } it { is_expected.to include(is_supported_content: 'false') } end diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb index 7989b863781..71ffa4a00a1 100644 --- a/spec/lib/gitlab/url_builder_spec.rb +++ b/spec/lib/gitlab/url_builder_spec.rb @@ -28,7 +28,6 @@ describe Gitlab::UrlBuilder do :group | ->(group) { "/groups/#{group.full_path}" } :group_milestone | ->(milestone) { "/groups/#{milestone.group.full_path}/-/milestones/#{milestone.iid}" } - :group_wiki | ->(wiki) { "/groups/#{wiki.container.full_path}/-/wikis/home" } :user | ->(user) { "/#{user.full_path}" } :personal_snippet | ->(snippet) { "/snippets/#{snippet.id}" } diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 04801983d6c..6e2cc10ab0c 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -27,11 +27,6 @@ describe Group do it { is_expected.to have_many(:milestones) } it { is_expected.to have_many(:sprints) } - it_behaves_like 'model with wiki' do - let(:container) { create(:group, :nested, :wiki_repo) } - let(:container_without_wiki) { create(:group, :nested) } - end - describe '#members & #requesters' do let(:requester) { create(:user) } let(:developer) { create(:user) } diff --git a/spec/models/group_wiki_spec.rb b/spec/models/group_wiki_spec.rb deleted file mode 100644 index ed6bac5311b..00000000000 --- a/spec/models/group_wiki_spec.rb +++ /dev/null @@ -1,38 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -describe GroupWiki do - it_behaves_like 'wiki model' do - let(:wiki_container) { create(:group, :wiki_repo) } - let(:wiki_container_without_repo) { create(:group) } - - before do - wiki_container.add_owner(user) - end - - describe '#storage' do - it 'uses the group repository prefix' do - expect(subject.storage.base_dir).to start_with('@groups/') - end - end - - describe '#repository_storage' do - it 'returns the default storage' do - expect(subject.repository_storage).to eq('default') - end - end - - describe '#hashed_storage?' do - it 'returns true' do - expect(subject.hashed_storage?).to be(true) - end - end - - describe '#disk_path' do - it 'returns the repository storage path' do - expect(subject.disk_path).to eq("#{subject.storage.disk_path}.wiki") - end - end - end -end diff --git a/spec/models/wiki_page_spec.rb b/spec/models/wiki_page_spec.rb index 99a2ce28d19..eb241fa123f 100644 --- a/spec/models/wiki_page_spec.rb +++ b/spec/models/wiki_page_spec.rb @@ -150,15 +150,7 @@ describe WikiPage do enable_front_matter_for(container) end - context 'with a project container' do - it_behaves_like 'a page with front-matter' - end - - context 'with a group container' do - let(:container) { create(:group) } - - it_behaves_like 'a page with front-matter' - end + it_behaves_like 'a page with front-matter' end end end @@ -512,15 +504,7 @@ describe WikiPage do enable_front_matter_for(container) end - context 'with a project container' do - it_behaves_like 'able to update front-matter' - end - - context 'with a group container' do - let(:container) { create(:group) } - - it_behaves_like 'able to update front-matter' - end + it_behaves_like 'able to update front-matter' end end @@ -826,22 +810,13 @@ describe WikiPage do expect(subject).not_to eq(other_page) end - it 'returns false for page with the same slug on a different container of the same type' do + it 'returns false for page with the same slug on a different container' do other_page = create(:wiki_page, title: existing_page.slug) expect(subject.slug).to eq(other_page.slug) expect(subject.container).not_to eq(other_page.container) expect(subject).not_to eq(other_page) end - - it 'returns false for page with the same slug on a different container type' do - group = create(:group, name: container.name) - other_page = create(:wiki_page, title: existing_page.slug, container: group) - - expect(subject.slug).to eq(other_page.slug) - expect(subject.container).not_to eq(other_page.container) - expect(subject).not_to eq(other_page) - end end describe '#last_commit_sha' do diff --git a/yarn.lock b/yarn.lock index 33f232fe284..0a4ef63d580 100644 --- a/yarn.lock +++ b/yarn.lock @@ -782,10 +782,10 @@ eslint-plugin-vue "^6.2.1" vue-eslint-parser "^7.0.0" -"@gitlab/svgs@1.123.0": - version "1.123.0" - resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.123.0.tgz#465946ae7afc486d6769dc38685f71747fa2fec7" - integrity sha512-lBTNnh7sEgUX3LVj6tEis9dcDDc5gKhCSUInGzswZVy9KeDAXbY850pKGPRKg/O1nVDPIe9yh7ieieWy25bkuQ== +"@gitlab/svgs@1.125.0": + version "1.125.0" + resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.125.0.tgz#59c667dae8f7e4c80b482f5f6cc35367c016387b" + integrity sha512-MKfFYa8f+9P2tJ/JN/E9oDBSSo/gRz2zuGui4XHQPoaw/DkIMn7EyAzeSpRgbgs1LgMcEqqKsIEx+spCga3jsQ== "@gitlab/ui@13.9.0": version "13.9.0" |