diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-05 19:58:23 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-03-05 19:58:23 +0000 |
commit | 09432c7f561449734e2ae298496dc0f1d7da0d6a (patch) | |
tree | b444ddf7f6f8d25aa1af899f9c1f9a41feafaec7 | |
parent | e2aba30891c3deff4174df08b92817095eec38d5 (diff) | |
download | gitlab-ce-09432c7f561449734e2ae298496dc0f1d7da0d6a.tar.gz |
Add latest changes from gitlab-org/gitlab@12-8-stable-ee
57 files changed, 568 insertions, 169 deletions
diff --git a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue index 70f257180c6..552e8cac3a7 100644 --- a/app/assets/javascripts/error_tracking/components/error_tracking_list.vue +++ b/app/assets/javascripts/error_tracking/components/error_tracking_list.vue @@ -236,6 +236,7 @@ export default { </gl-dropdown> <div class="filtered-search-input-container flex-fill"> <gl-form-input + v-model="errorSearchQuery" class="pl-2 filtered-search" :disabled="loading" :placeholder="__('Search or filter results…')" diff --git a/app/assets/javascripts/ide/lib/files.js b/app/assets/javascripts/ide/lib/files.js index bee867fa47c..26518a2abac 100644 --- a/app/assets/javascripts/ide/lib/files.js +++ b/app/assets/javascripts/ide/lib/files.js @@ -1,5 +1,4 @@ import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils'; -import { escapeFileUrl } from '~/lib/utils/url_utility'; import { decorateData, sortTree } from '../stores/utils'; export const splitParent = path => { @@ -48,7 +47,7 @@ export const decorateFiles = ({ id: path, name, path, - url: `/${projectId}/tree/${branchId}/-/${escapeFileUrl(path)}/`, + url: `/${projectId}/tree/${branchId}/-/${path}/`, type: 'tree', parentTreeUrl: parentFolder ? parentFolder.url : `/${projectId}/tree/${branchId}/`, tempFile, @@ -85,7 +84,7 @@ export const decorateFiles = ({ id: path, name, path, - url: `/${projectId}/blob/${branchId}/-/${escapeFileUrl(path)}`, + url: `/${projectId}/blob/${branchId}/-/${path}`, type: 'blob', parentTreeUrl: fileFolder ? fileFolder.url : `/${projectId}/blob/${branchId}`, tempFile, diff --git a/app/assets/javascripts/ide/stores/utils.js b/app/assets/javascripts/ide/stores/utils.js index 06e66da1069..4e5b01596d8 100644 --- a/app/assets/javascripts/ide/stores/utils.js +++ b/app/assets/javascripts/ide/stores/utils.js @@ -1,5 +1,4 @@ import { commitActionTypes, FILE_VIEW_MODE_EDITOR } from '../constants'; -import { escapeFileUrl } from '~/lib/utils/url_utility'; export const dataStructure = () => ({ id: '', @@ -220,9 +219,7 @@ export const mergeTrees = (fromTree, toTree) => { export const replaceFileUrl = (url, oldPath, newPath) => { // Add `/-/` so that we don't accidentally replace project path - const result = url.replace(`/-/${escapeFileUrl(oldPath)}`, `/-/${escapeFileUrl(newPath)}`); - - return result; + return url.replace(`/-/${oldPath}`, `/-/${newPath}`); }; export const swapInStateArray = (state, arr, key, entryPath) => diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js index b49fe9362c2..8d3b87d5cc0 100644 --- a/app/assets/javascripts/lib/graphql.js +++ b/app/assets/javascripts/lib/graphql.js @@ -26,6 +26,10 @@ export default (resolvers = {}, config = {}) => { headers: { [csrf.headerKey]: csrf.token, }, + // fetch won’t send cookies in older browsers, unless you set the credentials init option. + // We set to `same-origin` which is default value in modern browsers. + // See https://github.com/whatwg/fetch/pull/585 for more information. + credentials: 'same-origin', }; return new ApolloClient({ diff --git a/app/assets/javascripts/repository/components/breadcrumbs.vue b/app/assets/javascripts/repository/components/breadcrumbs.vue index 751565ad542..be70bfc7399 100644 --- a/app/assets/javascripts/repository/components/breadcrumbs.vue +++ b/app/assets/javascripts/repository/components/breadcrumbs.vue @@ -1,5 +1,6 @@ <script> import { GlDropdown, GlDropdownDivider, GlDropdownHeader, GlDropdownItem } from '@gitlab/ui'; +import { joinPaths } from '~/lib/utils/url_utility'; import { __ } from '../../locale'; import Icon from '../../vue_shared/components/icon.vue'; import getRefMixin from '../mixins/get_ref'; @@ -102,12 +103,12 @@ export default { .filter(p => p !== '') .reduce( (acc, name, i) => { - const path = `${i > 0 ? acc[i].path : ''}/${name}`; + const path = joinPaths(i > 0 ? acc[i].path : '', encodeURIComponent(name)); return acc.concat({ name, path, - to: `/-/tree/${escape(this.ref)}${escape(path)}`, + to: `/-/tree/${joinPaths(escape(this.ref), path)}`, }); }, [ diff --git a/app/assets/javascripts/repository/components/last_commit.vue b/app/assets/javascripts/repository/components/last_commit.vue index 968bd9af84f..64003630271 100644 --- a/app/assets/javascripts/repository/components/last_commit.vue +++ b/app/assets/javascripts/repository/components/last_commit.vue @@ -79,7 +79,7 @@ export default { return this.$apollo.queries.commit.loading; }, showCommitId() { - return this.commit.sha.substr(0, 8); + return this.commit?.sha?.substr(0, 8); }, }, watch: { diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue index c905c39bbba..b81e6a38b4c 100644 --- a/app/assets/javascripts/repository/components/table/row.vue +++ b/app/assets/javascripts/repository/components/table/row.vue @@ -91,7 +91,9 @@ export default { }, computed: { routerLinkTo() { - return this.isFolder ? { path: `/-/tree/${escape(this.ref)}/${escape(this.path)}` } : null; + return this.isFolder + ? { path: `/-/tree/${escape(this.ref)}/${encodeURIComponent(this.path)}` } + : null; }, iconName() { return `fa-${getIconName(this.type, this.path)}`; @@ -141,6 +143,7 @@ export default { <i v-else :aria-label="type" role="img" :class="iconName" class="fa fa-fw"></i> <component :is="linkComponent" + ref="link" :to="routerLinkTo" :href="url" class="str-truncated" diff --git a/app/assets/javascripts/repository/log_tree.js b/app/assets/javascripts/repository/log_tree.js index 192e410b36f..ade92cc92e0 100644 --- a/app/assets/javascripts/repository/log_tree.js +++ b/app/assets/javascripts/repository/log_tree.js @@ -27,7 +27,7 @@ export function fetchLogsTree(client, path, offset, resolver = null) { fetchpromise = axios .get( - `${gon.relative_url_root}/${projectPath}/-/refs/${escape(ref)}/logs_tree/${escape( + `${gon.relative_url_root}/${projectPath}/-/refs/${escape(ref)}/logs_tree/${encodeURIComponent( path.replace(/^\//, ''), )}`, { diff --git a/app/assets/javascripts/vue_shared/components/file_row.vue b/app/assets/javascripts/vue_shared/components/file_row.vue index 578fcc819b0..4d60cf5b1cc 100644 --- a/app/assets/javascripts/vue_shared/components/file_row.vue +++ b/app/assets/javascripts/vue_shared/components/file_row.vue @@ -1,6 +1,7 @@ <script> import FileHeader from '~/vue_shared/components/file_row_header.vue'; import FileIcon from '~/vue_shared/components/file_icon.vue'; +import { escapeFileUrl } from '~/lib/utils/url_utility'; export default { name: 'FileRow', @@ -94,7 +95,7 @@ export default { hasUrlAtCurrentRoute() { if (!this.$router || !this.$router.currentRoute) return true; - return this.$router.currentRoute.path === `/project${this.file.url}`; + return this.$router.currentRoute.path === `/project${escapeFileUrl(this.file.url)}`; }, }, }; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index f8832047d49..8b2c67378d9 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -1006,6 +1006,14 @@ pre.light-well { } } + &:not(.with-pipeline-status) { + .icon-wrapper:first-of-type { + @include media-breakpoint-up(lg) { + margin-left: $gl-padding-32; + } + } + } + .ci-status-link { display: inline-flex; } diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index db1b8c559e5..81e910f91c2 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -17,12 +17,6 @@ .tree-controls { text-align: right; - > .btn, - .project-action-button > .btn, - .git-clone-holder > .btn { - margin-left: 8px; - } - .control { float: left; margin-left: 10px; diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss index 0fd6aafef0d..e27ec571531 100644 --- a/app/assets/stylesheets/utilities.scss +++ b/app/assets/stylesheets/utilities.scss @@ -45,6 +45,12 @@ .border-bottom-color-default { border-bottom-color: $border-color; } .box-shadow-default { box-shadow: 0 2px 4px 0 $black-transparent; } +.gl-children-ml-sm-3 > * { + @include media-breakpoint-up(sm) { + @include gl-ml-3; + } +} + .mh-50vh { max-height: 50vh; } .font-size-inherit { font-size: inherit; } diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb index b3d72ebdcf3..0a536a01f72 100644 --- a/app/models/broadcast_message.rb +++ b/app/models/broadcast_message.rb @@ -52,7 +52,9 @@ class BroadcastMessage < ApplicationRecord end def cache - Gitlab::JsonCache.new(cache_key_with_version: false) + ::Gitlab::SafeRequestStore.fetch(:broadcast_message_json_cache) do + Gitlab::JsonCache.new(cache_key_with_version: false) + end end def cache_expires_in @@ -68,9 +70,9 @@ class BroadcastMessage < ApplicationRecord now_or_future = messages.select(&:now_or_future?) - # If there are cached entries but none are to be displayed we'll purge the - # cache so we don't keep running this code all the time. - cache.expire(cache_key) if now_or_future.empty? + # If there are cached entries but they don't match the ones we are + # displaying we'll refresh the cache so we don't need to keep filtering. + cache.expire(cache_key) if now_or_future != messages now_or_future.select(&:now?).select { |message| message.matches_current_path(current_path) } end diff --git a/app/uploaders/import_export_uploader.rb b/app/uploaders/import_export_uploader.rb index 104d5d3b3dd..b0e6464f5b1 100644 --- a/app/uploaders/import_export_uploader.rb +++ b/app/uploaders/import_export_uploader.rb @@ -3,6 +3,10 @@ class ImportExportUploader < AttachmentUploader EXTENSION_WHITELIST = %w[tar.gz gz].freeze + def self.workhorse_local_upload_path + File.join(options.storage_path, 'uploads', TMP_UPLOAD_PATH) + end + def extension_whitelist EXTENSION_WHITELIST end diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml index ca201e626b8..5122164dbcb 100644 --- a/app/views/dashboard/projects/_projects.html.haml +++ b/app/views/dashboard/projects/_projects.html.haml @@ -1 +1 @@ -= render 'shared/projects/list', projects: @projects, user: current_user += render 'shared/projects/list', projects: @projects, pipeline_status: Feature.enabled?(:dashboard_pipeline_status, default_enabled: true), user: current_user diff --git a/app/views/explore/projects/_projects.html.haml b/app/views/explore/projects/_projects.html.haml index 35b32662b8a..d819c4ea554 100644 --- a/app/views/explore/projects/_projects.html.haml +++ b/app/views/explore/projects/_projects.html.haml @@ -1,2 +1,2 @@ - is_explore_page = defined?(explore_page) && explore_page -= render 'shared/projects/list', projects: projects, user: current_user, explore_page: is_explore_page += render 'shared/projects/list', projects: projects, user: current_user, explore_page: is_explore_page, pipeline_status: Feature.enabled?(:dashboard_pipeline_status, default_enabled: true) diff --git a/app/views/projects/blob/_breadcrumb.html.haml b/app/views/projects/blob/_breadcrumb.html.haml index e611df8df2a..810c8b9082f 100644 --- a/app/views/projects/blob/_breadcrumb.html.haml +++ b/app/views/projects/blob/_breadcrumb.html.haml @@ -17,7 +17,7 @@ - else = link_to title, project_tree_path(@project, tree_join(@ref, path)) - .tree-controls< + .tree-controls.gl-children-ml-sm-3< = render 'projects/find_file_link' -# only show normal/blame view links for text files - if blob.readable_text? diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 4d3c24aee6b..d5f7673488f 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -75,34 +75,35 @@ = link_to new_project_tag_path(@project) do #{ _('New tag') } -.tree-controls{ class: ("gl-font-size-0" if vue_file_list_enabled?) }< - = render_if_exists 'projects/tree/lock_link' - - if vue_file_list_enabled? - #js-tree-history-link.d-inline-block{ data: { history_link: project_commits_path(@project, @ref) } } - - else - = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' - - = render 'projects/find_file_link' - - - if can_collaborate || current_user&.already_forked?(@project) +.tree-controls + .d-block.d-sm-flex.flex-wrap.align-items-start.gl-children-ml-sm-3< + = render_if_exists 'projects/tree/lock_link' - if vue_file_list_enabled? - #js-tree-web-ide-link.d-inline-block + #js-tree-history-link.d-inline-block{ data: { history_link: project_commits_path(@project, @ref) } } - else - = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do + = link_to s_('Commits|History'), project_commits_path(@project, @id), class: 'btn' + + = render 'projects/find_file_link' + + - if can_collaborate || current_user&.already_forked?(@project) + - if vue_file_list_enabled? + #js-tree-web-ide-link.d-inline-block + - else + = link_to ide_edit_path(@project, @ref, @path), class: 'btn btn-default qa-web-ide-button' do + = _('Web IDE') + - elsif can_create_mr_from_fork + = link_to '#modal-confirm-fork', class: 'btn btn-default qa-web-ide-button', data: { target: '#modal-confirm-fork', toggle: 'modal'} do = _('Web IDE') - - elsif can_create_mr_from_fork - = link_to '#modal-confirm-fork', class: 'btn btn-default qa-web-ide-button', data: { target: '#modal-confirm-fork', toggle: 'modal'} do - = _('Web IDE') - = render 'shared/confirm_fork_modal', fork_path: ide_fork_and_edit_path(@project, @ref, @path) + = render 'shared/confirm_fork_modal', fork_path: ide_fork_and_edit_path(@project, @ref, @path) - - if show_xcode_link?(@project) - .project-action-button.project-xcode.inline< - = render "projects/buttons/xcode_link" + - if show_xcode_link?(@project) + .project-action-button.project-xcode.inline< + = render "projects/buttons/xcode_link" - = render 'projects/buttons/download', project: @project, ref: @ref + = render 'projects/buttons/download', project: @project, ref: @ref - .project-clone-holder.d-block.d-md-none.mt-sm-2.mt-md-0> - = render "shared/mobile_clone_panel" + .project-clone-holder.d-none.d-md-inline-block> + = render "projects/buttons/clone", dropdown_class: 'dropdown-menu-right' - .project-clone-holder.d-none.d-md-inline-block> - = render "projects/buttons/clone", dropdown_class: 'dropdown-menu-right' + .project-clone-holder.d-block.d-md-none.mt-sm-2.mt-md-0.ml-sm-2> + = render "shared/mobile_clone_panel" diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml index 07a61b71b8e..45e95685677 100644 --- a/app/views/shared/projects/_project.html.haml +++ b/app/views/shared/projects/_project.html.haml @@ -12,7 +12,9 @@ - css_class += " no-description" if project.description.blank? && !show_last_commit_as_description - cache_key = project_list_cache_key(project, pipeline_status: pipeline_status) - updated_tooltip = time_ago_with_tooltip(project.last_activity_date) +- show_pipeline_status_icon = pipeline_status && can?(current_user, :read_cross_project) && project.pipeline_status.has_status? && can?(current_user, :read_build, project) - css_controls_class = compact_mode ? [] : ["flex-lg-row", "justify-content-lg-between"] +- css_controls_class << "with-pipeline-status" if show_pipeline_status_icon - avatar_container_class = project.creator && use_creator_avatar ? '' : 'rect-avatar' %li.project-row.d-flex{ class: css_class } @@ -60,6 +62,11 @@ .controls.d-flex.flex-sm-column.align-items-center.align-items-sm-end.flex-wrap.flex-shrink-0.text-secondary{ class: css_controls_class.join(" ") } .icon-container.d-flex.align-items-center + - if show_pipeline_status_icon + - pipeline_path = pipelines_project_commit_path(project.pipeline_status.project, project.pipeline_status.sha, ref: project.pipeline_status.ref) + %span.icon-wrapper.pipeline-status + = render 'ci/status/icon', status: project.last_pipeline.detailed_status(current_user), tooltip_placement: 'top', path: pipeline_path + = render_if_exists 'shared/projects/archived', project: project - if stars = link_to project_starrers_path(project), diff --git a/changelogs/unreleased/207837-circular-encoding.yml b/changelogs/unreleased/207837-circular-encoding.yml new file mode 100644 index 00000000000..30aa7bf88a5 --- /dev/null +++ b/changelogs/unreleased/207837-circular-encoding.yml @@ -0,0 +1,5 @@ +--- +title: Fixed regression when URL was encoded in a loop +merge_request: 25849 +author: +type: fixed diff --git a/changelogs/unreleased/207857-fix-web-ide-modal-no-text.yml b/changelogs/unreleased/207857-fix-web-ide-modal-no-text.yml new file mode 100644 index 00000000000..74bbb312f19 --- /dev/null +++ b/changelogs/unreleased/207857-fix-web-ide-modal-no-text.yml @@ -0,0 +1,5 @@ +--- +title: Fix Web IDE fork modal showing no text +merge_request: 25842 +author: +type: fixed diff --git a/changelogs/unreleased/georgekoltsov-fix-import-export-uploader.yml b/changelogs/unreleased/georgekoltsov-fix-import-export-uploader.yml new file mode 100644 index 00000000000..0c43c93ce89 --- /dev/null +++ b/changelogs/unreleased/georgekoltsov-fix-import-export-uploader.yml @@ -0,0 +1,5 @@ +--- +title: Fix Group Import API file upload when object storage is disabled +merge_request: 25715 +author: +type: fixed diff --git a/changelogs/unreleased/lm-fix-error-query.yml b/changelogs/unreleased/lm-fix-error-query.yml new file mode 100644 index 00000000000..2baa316dd5a --- /dev/null +++ b/changelogs/unreleased/lm-fix-error-query.yml @@ -0,0 +1,5 @@ +--- +title: Fix search for Sentry error list +merge_request: 26129 +author: +type: fixed diff --git a/changelogs/unreleased/ph-p207499-fixNonAsciiChars.yml b/changelogs/unreleased/ph-p207499-fixNonAsciiChars.yml new file mode 100644 index 00000000000..806f4372f89 --- /dev/null +++ b/changelogs/unreleased/ph-p207499-fixNonAsciiChars.yml @@ -0,0 +1,5 @@ +--- +title: Fixed repository browsing for folders with non-ascii characters +merge_request: 25877 +author: +type: fixed diff --git a/changelogs/unreleased/revert-e0613e64.yml b/changelogs/unreleased/revert-e0613e64.yml new file mode 100644 index 00000000000..e94f1df2f4b --- /dev/null +++ b/changelogs/unreleased/revert-e0613e64.yml @@ -0,0 +1,5 @@ +--- +title: Show CI status in project dashboards +merge_request: 26403 +author: +type: fixed diff --git a/changelogs/unreleased/sh-disable-line-in-marginalia.yml b/changelogs/unreleased/sh-disable-line-in-marginalia.yml new file mode 100644 index 00000000000..51be4db1965 --- /dev/null +++ b/changelogs/unreleased/sh-disable-line-in-marginalia.yml @@ -0,0 +1,5 @@ +--- +title: Disable Marginalia line backtrace in production +merge_request: 26199 +author: +type: performance diff --git a/changelogs/unreleased/sh-optimize-broadcast-message-redis.yml b/changelogs/unreleased/sh-optimize-broadcast-message-redis.yml new file mode 100644 index 00000000000..c69f3ded73d --- /dev/null +++ b/changelogs/unreleased/sh-optimize-broadcast-message-redis.yml @@ -0,0 +1,5 @@ +--- +title: Remove unnecessary Redis deletes for broadcast messages +merge_request: 26541 +author: +type: performance diff --git a/changelogs/unreleased/use-addressable-for-asset-proxy-badge-render.yml b/changelogs/unreleased/use-addressable-for-asset-proxy-badge-render.yml new file mode 100644 index 00000000000..6a021df8027 --- /dev/null +++ b/changelogs/unreleased/use-addressable-for-asset-proxy-badge-render.yml @@ -0,0 +1,5 @@ +--- +title: Rescue invalid URLs during badge retrieval in asset proxy +merge_request: 26524 +author: +type: fixed diff --git a/changelogs/unreleased/vs-add-credentials-option-for-apollo-link.yml b/changelogs/unreleased/vs-add-credentials-option-for-apollo-link.yml new file mode 100644 index 00000000000..1863f0de26f --- /dev/null +++ b/changelogs/unreleased/vs-add-credentials-option-for-apollo-link.yml @@ -0,0 +1,5 @@ +--- +title: Send credentials with GraphQL fetch requests +merge_request: 26386 +author: +type: fixed diff --git a/config/initializers/0_marginalia.rb b/config/initializers/0_marginalia.rb index f88a90854e3..a697f67dbf2 100644 --- a/config/initializers/0_marginalia.rb +++ b/config/initializers/0_marginalia.rb @@ -9,7 +9,13 @@ require 'marginalia' # Refer: https://github.com/basecamp/marginalia/blob/v1.8.0/lib/marginalia/railtie.rb#L67 ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(Gitlab::Marginalia::ActiveRecordInstrumentation) -Marginalia::Comment.components = [:application, :controller, :action, :correlation_id, :jid, :job_class, :line] +Marginalia::Comment.components = [:application, :controller, :action, :correlation_id, :jid, :job_class] + +# As mentioned in https://github.com/basecamp/marginalia/pull/93/files, +# adding :line has some overhead because a regexp on the backtrace has +# to be run on every SQL query. Only enable this in development because +# we've seen it slow things down. +Marginalia::Comment.components << :line if Rails.env.development? Gitlab::Marginalia.set_application_name diff --git a/doc/user/incident_management/index.md b/doc/user/incident_management/index.md index c644c5801df..880083bf815 100644 --- a/doc/user/incident_management/index.md +++ b/doc/user/incident_management/index.md @@ -65,6 +65,11 @@ alert is resolved. Metrics can be embedded anywhere where GitLab Markdown is used, for example, descriptions and comments on issues and merge requests. +This can be useful for when you're sharing metrics, such as for discussing +an incident or performance issues, so you can output the dashboard directly +into any issue, merge request, epic, or any other Markdown text field in GitLab +by simply [copying and pasting the link to the metrics dashboard](../project/integrations/prometheus.md#embedding-gitlab-managed-kubernetes-metrics). + TIP: **Tip:** Both GitLab-hosted and Grafana metrics can also be [embedded in issue templates](../project/integrations/prometheus.md#embedding-metrics-in-issue-templates). @@ -73,6 +78,32 @@ Both GitLab-hosted and Grafana metrics can also be Learn how to embed [GitLab hosted metric charts](../project/integrations/prometheus.md#embedding-metric-charts-within-gitlab-flavored-markdown). +#### Context menu + +From each of the embedded metrics panels, you can access more details +about the data you are viewing from a context menu. + +You can access the context menu by clicking the **{ellipsis_v}** **More actions** +dropdown box above the upper right corner of the panel: + +The options are: + +- [View logs](#view-logs-ultimate) **(ULTIMATE)** +- [Download CSV](#download-csv) + +##### View logs **(ULTIMATE)** + +> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/201846) in GitLab Ultimate 12.8. + +This can be useful if you are triaging an application incident and need to +[explore logs](../project/integrations/prometheus.md#view-pod-logs-ultimate) +from across your application. It also helps you to understand +what is affecting your application's performance and quickly resolve any problems. + +##### Download CSV + +Data from embedded charts can be [downloaded as CSV](../project/integrations/prometheus.md#downloading-data-as-csv). + ### Grafana metrics Learn how to embed [Grafana hosted metric charts](../project/integrations/prometheus.md#embedding-grafana-charts). diff --git a/doc/user/project/integrations/img/embedded_metrics_markdown_v12_8.png b/doc/user/project/integrations/img/embedded_metrics_markdown_v12_8.png Binary files differnew file mode 100644 index 00000000000..ffd34705464 --- /dev/null +++ b/doc/user/project/integrations/img/embedded_metrics_markdown_v12_8.png diff --git a/doc/user/project/integrations/img/embedded_metrics_rendered_v12_8.png b/doc/user/project/integrations/img/embedded_metrics_rendered_v12_8.png Binary files differnew file mode 100644 index 00000000000..b024daaaa8e --- /dev/null +++ b/doc/user/project/integrations/img/embedded_metrics_rendered_v12_8.png diff --git a/doc/user/project/integrations/prometheus.md b/doc/user/project/integrations/prometheus.md index b8ba27bb2ca..a1c551db604 100644 --- a/doc/user/project/integrations/prometheus.md +++ b/doc/user/project/integrations/prometheus.md @@ -492,7 +492,9 @@ The options are: > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/122013) in GitLab 12.8. -If you have [Kubernetes Pod Logs](../clusters/kubernetes_pod_logs.md) enabled, you can navigate from the charts in the dashboard to view Pod Logs by clicking on the context menu in the upper-right corner. +If you have [Pod Logs](../clusters/kubernetes_pod_logs.md) enabled, +you can navigate from the charts in the dashboard to view Pod Logs by +clicking on the context menu in the upper-right corner. If you use the **Timeline zoom** function at the bottom of the chart, logs will narrow down to the time range you selected. @@ -608,10 +610,19 @@ Prometheus server. It is possible to display metrics charts within [GitLab Flavored Markdown](../../markdown.md#gitlab-flavored-markdown-gfm). The maximum number of embeds allowed in a GitLab Flavored Markdown field is 100. +This can be useful if you are sharing an application incident or performance +metrics to others and want to have relevant information directly available. + NOTE: **Note:** Requires [Kubernetes](prometheus_library/kubernetes.md) metrics. -To display a metric chart, include a link of the form `https://<root_url>/<project>/-/environments/<environment_id>/metrics`. +To display metric charts, include a link of the form `https://<root_url>/<project>/-/environments/<environment_id>/metrics`: + +![Embedded Metrics Markdown](img/embedded_metrics_markdown_v12_8.png) + +GitLab unfurls the link as an embedded metrics panel: + +![Embedded Metrics Rendered](img/embedded_metrics_rendered_v12_8.png) A single chart may also be embedded. You can generate a link to the chart via the dropdown located on the right side of the chart: diff --git a/lib/gitlab/asset_proxy.rb b/lib/gitlab/asset_proxy.rb index fd7c58ba68f..fb4369e01d8 100644 --- a/lib/gitlab/asset_proxy.rb +++ b/lib/gitlab/asset_proxy.rb @@ -11,12 +11,14 @@ module Gitlab return url if asset_host_whitelisted?(url) "#{Gitlab.config.asset_proxy.url}/#{asset_url_hash(url)}/#{hexencode(url)}" + rescue Addressable::URI::InvalidURIError + url end private def asset_host_whitelisted?(url) - parsed_url = URI.parse(url) + parsed_url = Addressable::URI.parse(url) Gitlab.config.asset_proxy.domain_regexp&.match?(parsed_url.host) end diff --git a/lib/gitlab/git/rugged_impl/repository.rb b/lib/gitlab/git/rugged_impl/repository.rb index 8fde93e71e2..8679d977773 100644 --- a/lib/gitlab/git/rugged_impl/repository.rb +++ b/lib/gitlab/git/rugged_impl/repository.rb @@ -70,7 +70,7 @@ module Gitlab # Lookup for rugged object by oid or ref name def lookup(oid_or_ref_name) - rugged.rev_parse(oid_or_ref_name) + rev_parse_target(oid_or_ref_name) end end end diff --git a/lib/gitlab/tree_summary.rb b/lib/gitlab/tree_summary.rb index 76018cb23c4..5df53b5adde 100644 --- a/lib/gitlab/tree_summary.rb +++ b/lib/gitlab/tree_summary.rb @@ -69,7 +69,7 @@ module Gitlab end def entry_path(entry) - File.join(*[path, entry[:file_name]].compact) + File.join(*[path, entry[:file_name]].compact).force_encoding(Encoding::ASCII_8BIT) end def build_entry(entry) diff --git a/spec/features/dashboard/projects_spec.rb b/spec/features/dashboard/projects_spec.rb index 9bd2e85e3b8..73f759f8a54 100644 --- a/spec/features/dashboard/projects_spec.rb +++ b/spec/features/dashboard/projects_spec.rb @@ -152,6 +152,61 @@ describe 'Dashboard Projects' do end end + describe 'with a pipeline', :clean_gitlab_redis_shared_state do + let(:pipeline) { create(:ci_pipeline, project: project, sha: project.commit.sha, ref: project.default_branch) } + + before do + # Since the cache isn't updated when a new pipeline is created + # we need the pipeline to advance in the pipeline since the cache was created + # by visiting the login page. + pipeline.succeed + end + + it 'shows that the last pipeline passed' do + visit dashboard_projects_path + + page.within('.controls') do + expect(page).to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']") + expect(page).to have_css('.ci-status-link') + expect(page).to have_css('.ci-status-icon-success') + expect(page).to have_link('Pipeline: passed') + end + end + + shared_examples 'hidden pipeline status' do + it 'does not show the pipeline status' do + visit dashboard_projects_path + + page.within('.controls') do + expect(page).not_to have_xpath("//a[@href='#{pipelines_project_commit_path(project, project.commit, ref: pipeline.ref)}']") + expect(page).not_to have_css('.ci-status-link') + expect(page).not_to have_css('.ci-status-icon-success') + expect(page).not_to have_link('Pipeline: passed') + end + end + end + + context 'guest user of project and project has private pipelines' do + let(:guest_user) { create(:user) } + + before do + project.update(public_builds: false) + project.add_guest(guest_user) + sign_in(guest_user) + end + + it_behaves_like 'hidden pipeline status' + end + + context 'when dashboard_pipeline_status is disabled' do + before do + stub_feature_flags(dashboard_pipeline_status: false) + end + + it_behaves_like 'hidden pipeline status' + end + end + context 'last push widget', :use_clean_rails_memory_store_caching do before do event = create(:push_event, project: project, author: user) diff --git a/spec/features/error_tracking/user_searches_sentry_errors_spec.rb b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb new file mode 100644 index 00000000000..690c60a3c3f --- /dev/null +++ b/spec/features/error_tracking/user_searches_sentry_errors_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe 'When a user searches for Sentry errors', :js, :use_clean_rails_memory_store_caching, :sidekiq_inline do + include_context 'sentry error tracking context feature' + + let_it_be(:issues_response_body) { fixture_file('sentry/issues_sample_response.json') } + let_it_be(:error_search_response_body) { fixture_file('sentry/error_list_search_response.json') } + let(:issues_api_url) { "#{sentry_api_urls.issues_url}?limit=20&query=is:unresolved" } + let(:issues_api_url_search) { "#{sentry_api_urls.issues_url}?limit=20&query=is:unresolved%20NotFound" } + + before do + stub_request(:get, issues_api_url).with( + headers: { 'Authorization' => 'Bearer access_token_123' } + ).to_return(status: 200, body: issues_response_body, headers: { 'Content-Type' => 'application/json' }) + + stub_request(:get, issues_api_url_search).with( + headers: { 'Authorization' => 'Bearer access_token_123', 'Content-Type' => 'application/json' } + ).to_return(status: 200, body: error_search_response_body, headers: { 'Content-Type' => 'application/json' }) + end + + it 'displays the results' do + sign_in(project.owner) + visit project_error_tracking_index_path(project) + + page.within(find('.gl-table')) do + results = page.all('.table-row') + expect(results.count).to be(2) + end + + find('.gl-form-input').set('NotFound').native.send_keys(:return) + + page.within(find('.gl-table')) do + results = page.all('.table-row') + expect(results.count).to be(1) + expect(results.first).to have_content('NotFound') + end + end +end diff --git a/spec/features/projects/tree/tree_show_spec.rb b/spec/features/projects/tree/tree_show_spec.rb index 23b13858096..2407a9e6ea3 100644 --- a/spec/features/projects/tree/tree_show_spec.rb +++ b/spec/features/projects/tree/tree_show_spec.rb @@ -37,6 +37,16 @@ describe 'Projects tree', :js do expect(page).not_to have_selector('.flash-alert') end + it 'renders tree table with non-ASCII filenames without errors' do + visit project_tree_path(project, File.join(test_sha, 'encoding')) + wait_for_requests + + expect(page).to have_selector('.tree-item') + expect(page).to have_content('Files, encoding and much more') + expect(page).to have_content('テスト.txt') + expect(page).not_to have_selector('.flash-alert') + end + context 'gravatar disabled' do let(:gravatar_enabled) { false } diff --git a/spec/fixtures/sentry/error_list_search_response.json b/spec/fixtures/sentry/error_list_search_response.json new file mode 100644 index 00000000000..e77c837b48c --- /dev/null +++ b/spec/fixtures/sentry/error_list_search_response.json @@ -0,0 +1,42 @@ +[{ + "lastSeen": "2018-12-31T12:00:11Z", + "numComments": 0, + "userCount": 0, + "stats": { + "24h": [ + [ + 1546437600, + 0 + ] + ] + }, + "culprit": "sentry.tasks.reports.deliver_organization_user_report", + "title": "NotFound desc = GetRepoPath: not a git repository", + "id": "13", + "assignedTo": null, + "logger": null, + "type": "error", + "annotations": [], + "metadata": { + "type": "gaierror", + "value": "[Errno -2] Name or service not known" + }, + "status": "unresolved", + "subscriptionDetails": null, + "isPublic": false, + "hasSeen": false, + "shortId": "INTERNAL-4", + "shareId": null, + "firstSeen": "2018-12-17T12:00:14Z", + "count": "17283712", + "permalink": "35.228.54.90/sentry/internal/issues/13/", + "level": "error", + "isSubscribed": true, + "isBookmarked": false, + "project": { + "slug": "internal", + "id": "1", + "name": "Internal" + }, + "statusDetails": {} +}] diff --git a/spec/fixtures/sentry/issues_sample_response.json b/spec/fixtures/sentry/issues_sample_response.json index ed22499cfa1..495562ac960 100644 --- a/spec/fixtures/sentry/issues_sample_response.json +++ b/spec/fixtures/sentry/issues_sample_response.json @@ -1,4 +1,5 @@ -[{ +[ + { "lastSeen": "2018-12-31T12:00:11Z", "numComments": 0, "userCount": 0, @@ -39,4 +40,47 @@ "name": "Internal" }, "statusDetails": {} - }] + }, + { + "lastSeen": "2018-12-31T12:00:11Z", + "numComments": 0, + "userCount": 0, + "stats": { + "24h": [ + [ + 1546437600, + 0 + ] + ] + }, + "culprit": "sentry.tasks.reports.deliver_organization_user_report", + "title": "NotFound desc = GetRepoPath: not a git repository", + "id": "13", + "assignedTo": null, + "logger": null, + "type": "error", + "annotations": [], + "metadata": { + "type": "gaierror", + "value": "GetRepoPath: not a git repository" + }, + "status": "unresolved", + "subscriptionDetails": null, + "isPublic": false, + "hasSeen": false, + "shortId": "INTERNAL-4", + "shareId": null, + "firstSeen": "2018-12-17T12:00:14Z", + "count": "17283712", + "permalink": "35.228.54.90/sentry/internal/issues/13/", + "level": "error", + "isSubscribed": true, + "isBookmarked": false, + "project": { + "slug": "internal", + "id": "1", + "name": "Internal" + }, + "statusDetails": {} + } +] diff --git a/spec/frontend/error_tracking/components/error_tracking_list_spec.js b/spec/frontend/error_tracking/components/error_tracking_list_spec.js index b632b461eb9..f852a3091aa 100644 --- a/spec/frontend/error_tracking/components/error_tracking_list_spec.js +++ b/spec/frontend/error_tracking/components/error_tracking_list_spec.js @@ -42,9 +42,6 @@ describe('ErrorTrackingList', () => { ...stubChildren(ErrorTrackingList), ...stubs, }, - data() { - return { errorSearchQuery: 'search' }; - }, }); } @@ -164,8 +161,9 @@ describe('ErrorTrackingList', () => { }); it('it searches by query', () => { + findSearchBox().vm.$emit('input', 'search'); findSearchBox().trigger('keyup.enter'); - expect(actions.searchByQuery.mock.calls[0][1]).toEqual(wrapper.vm.errorSearchQuery); + expect(actions.searchByQuery.mock.calls[0][1]).toBe('search'); }); it('it sorts by fields', () => { diff --git a/spec/frontend/ide/lib/files_spec.js b/spec/frontend/ide/lib/files_spec.js index 34eb57ae0d3..2b15aef6454 100644 --- a/spec/frontend/ide/lib/files_spec.js +++ b/spec/frontend/ide/lib/files_spec.js @@ -1,7 +1,6 @@ import { viewerInformationForPath } from '~/vue_shared/components/content_viewer/lib/viewer_utils'; import { decorateFiles, splitParent } from '~/ide/lib/files'; import { decorateData } from '~/ide/stores/utils'; -import { escapeFileUrl } from '~/lib/utils/url_utility'; const TEST_BRANCH_ID = 'lorem-ipsum'; const TEST_PROJECT_ID = 10; @@ -22,7 +21,7 @@ const createEntries = paths => { id: path, name, path, - url: createUrl(`/${TEST_PROJECT_ID}/${type}/${TEST_BRANCH_ID}/-/${escapeFileUrl(path)}`), + url: createUrl(`/${TEST_PROJECT_ID}/${type}/${TEST_BRANCH_ID}/-/${path}`), type, previewMode, binary: (previewMode && previewMode.binary) || false, diff --git a/spec/frontend/ide/stores/mutations_spec.js b/spec/frontend/ide/stores/mutations_spec.js index 9fe75d596fb..d9ce59ad378 100644 --- a/spec/frontend/ide/stores/mutations_spec.js +++ b/spec/frontend/ide/stores/mutations_spec.js @@ -494,7 +494,7 @@ describe('Multi-file store mutations', () => { it('properly handles files with spaces in name', () => { const path = 'my fancy path'; const newPath = 'new path'; - const oldEntry = { ...file(path, path, 'blob'), url: `project/-/${encodeURI(path)}` }; + const oldEntry = { ...file(path, path, 'blob'), url: `project/-/${path}` }; localState.entries[path] = oldEntry; @@ -510,12 +510,12 @@ describe('Multi-file store mutations', () => { id: newPath, path: newPath, name: newPath, - url: `project/-/new%20path`, + url: `project/-/new path`, key: expect.stringMatching(newPath), prevId: path, prevName: path, prevPath: path, - prevUrl: `project/-/my%20fancy%20path`, + prevUrl: `project/-/my fancy path`, prevKey: oldEntry.key, prevParentPath: oldEntry.parentPath, }); diff --git a/spec/frontend/repository/components/breadcrumbs_spec.js b/spec/frontend/repository/components/breadcrumbs_spec.js index 0271db25468..38e5c9aaca5 100644 --- a/spec/frontend/repository/components/breadcrumbs_spec.js +++ b/spec/frontend/repository/components/breadcrumbs_spec.js @@ -41,7 +41,7 @@ describe('Repository breadcrumbs component', () => { .findAll(RouterLinkStub) .at(3) .props('to'), - ).toEqual('/-/tree//app/assets/javascripts%23'); + ).toEqual('/-/tree/app/assets/javascripts%23'); }); it('renders last link as active', () => { diff --git a/spec/frontend/repository/components/table/row_spec.js b/spec/frontend/repository/components/table/row_spec.js index fec9ba3aa2e..a51846023ac 100644 --- a/spec/frontend/repository/components/table/row_spec.js +++ b/spec/frontend/repository/components/table/row_spec.js @@ -109,6 +109,26 @@ describe('Repository table row component', () => { }); }); + it.each` + path + ${'test#'} + ${'Änderungen'} + `('renders link for $path', ({ path }) => { + factory({ + id: '1', + sha: '123', + path, + type: 'tree', + currentPath: '/', + }); + + return vm.vm.$nextTick().then(() => { + expect(vm.find({ ref: 'link' }).props('to')).toEqual({ + path: `/-/tree/master/${encodeURIComponent(path)}`, + }); + }); + }); + it('pushes new route for directory with hash', () => { factory({ id: '1', diff --git a/spec/frontend/vue_shared/components/file_row_spec.js b/spec/frontend/vue_shared/components/file_row_spec.js new file mode 100644 index 00000000000..b3ced84ddb5 --- /dev/null +++ b/spec/frontend/vue_shared/components/file_row_spec.js @@ -0,0 +1,117 @@ +import { file } from 'jest/ide/helpers'; +import FileRow from '~/vue_shared/components/file_row.vue'; +import FileHeader from '~/vue_shared/components/file_row_header.vue'; +import { shallowMount } from '@vue/test-utils'; +import { nextTick } from 'vue'; +import { escapeFileUrl } from '~/lib/utils/url_utility'; + +describe('File row component', () => { + let wrapper; + + function createComponent(propsData, $router = undefined) { + wrapper = shallowMount(FileRow, { + propsData, + mocks: { + $router, + }, + }); + } + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders name', () => { + const fileName = 't4'; + createComponent({ + file: file(fileName), + level: 0, + }); + + const name = wrapper.find('.file-row-name'); + + expect(name.text().trim()).toEqual(fileName); + }); + + it('emits toggleTreeOpen on click', () => { + const fileName = 't3'; + createComponent({ + file: { + ...file(fileName), + type: 'tree', + }, + level: 0, + }); + jest.spyOn(wrapper.vm, '$emit'); + + wrapper.element.click(); + + expect(wrapper.vm.$emit).toHaveBeenCalledWith('toggleTreeOpen', fileName); + }); + + it('calls scrollIntoView if made active', () => { + createComponent({ + file: { + ...file(), + type: 'blob', + active: false, + }, + level: 0, + }); + + jest.spyOn(wrapper.vm, 'scrollIntoView'); + + wrapper.setProps({ + file: Object.assign({}, wrapper.props('file'), { + active: true, + }), + }); + + return nextTick().then(() => { + expect(wrapper.vm.scrollIntoView).toHaveBeenCalled(); + }); + }); + + it('indents row based on level', () => { + createComponent({ + file: file('t4'), + level: 2, + }); + + expect(wrapper.find('.file-row-name').element.style.marginLeft).toBe('32px'); + }); + + it('renders header for file', () => { + createComponent({ + file: { + isHeader: true, + path: 'app/assets', + tree: [], + }, + level: 0, + }); + + expect(wrapper.contains(FileHeader)).toBe(true); + }); + + it('matches the current route against encoded file URL', () => { + const fileName = 'with space'; + const rowFile = Object.assign({}, file(fileName), { + url: `/${fileName}`, + }); + const routerPath = `/project/${escapeFileUrl(fileName)}`; + createComponent( + { + file: rowFile, + level: 0, + }, + { + currentRoute: { + path: routerPath, + }, + }, + ); + + expect(wrapper.vm.hasUrlAtCurrentRoute()).toBe(true); + }); +}); diff --git a/spec/javascripts/vue_shared/components/file_row_spec.js b/spec/javascripts/vue_shared/components/file_row_spec.js deleted file mode 100644 index 11fcb9b89c1..00000000000 --- a/spec/javascripts/vue_shared/components/file_row_spec.js +++ /dev/null @@ -1,87 +0,0 @@ -import Vue from 'vue'; -import { file } from 'spec/ide/helpers'; -import FileRow from '~/vue_shared/components/file_row.vue'; -import mountComponent from '../../helpers/vue_mount_component_helper'; - -describe('File row component', () => { - let vm; - - function createComponent(propsData) { - const FileRowComponent = Vue.extend(FileRow); - - vm = mountComponent(FileRowComponent, propsData); - } - - afterEach(() => { - vm.$destroy(); - }); - - it('renders name', () => { - createComponent({ - file: file('t4'), - level: 0, - }); - - const name = vm.$el.querySelector('.file-row-name'); - - expect(name.textContent.trim()).toEqual(vm.file.name); - }); - - it('emits toggleTreeOpen on click', () => { - createComponent({ - file: { - ...file('t3'), - type: 'tree', - }, - level: 0, - }); - spyOn(vm, '$emit').and.stub(); - - vm.$el.click(); - - expect(vm.$emit).toHaveBeenCalledWith('toggleTreeOpen', vm.file.path); - }); - - it('calls scrollIntoView if made active', done => { - createComponent({ - file: { - ...file(), - type: 'blob', - active: false, - }, - level: 0, - }); - - spyOn(vm, 'scrollIntoView').and.stub(); - - vm.file.active = true; - - vm.$nextTick(() => { - expect(vm.scrollIntoView).toHaveBeenCalled(); - - done(); - }); - }); - - it('indents row based on level', () => { - createComponent({ - file: file('t4'), - level: 2, - }); - - expect(vm.$el.querySelector('.file-row-name').style.marginLeft).toBe('32px'); - }); - - it('renders header for file', () => { - createComponent({ - file: { - isHeader: true, - path: 'app/assets', - tree: [], - }, - level: 0, - }); - - expect(vm.$el.classList).toContain('js-file-row-header'); - }); -}); diff --git a/spec/lib/gitlab/asset_proxy_spec.rb b/spec/lib/gitlab/asset_proxy_spec.rb index f5aa1819982..e406917a5a4 100644 --- a/spec/lib/gitlab/asset_proxy_spec.rb +++ b/spec/lib/gitlab/asset_proxy_spec.rb @@ -33,9 +33,15 @@ describe Gitlab::AssetProxy do expect(described_class.proxy_url(url)).to eq(proxied_url) end + it 'returns original URL for invalid domains' do + url = 'foo_bar://' + + expect(described_class.proxy_url(url)).to eq(url) + end + context 'whitelisted domain' do it 'returns original URL for single domain whitelist' do - url = 'http://gitlab.com/test.png' + url = 'http://gitlab.com/${default_branch}/test.png' expect(described_class.proxy_url(url)).to eq(url) end diff --git a/spec/lib/gitlab/tree_summary_spec.rb b/spec/lib/gitlab/tree_summary_spec.rb index e15463ed0eb..d64b826ba9b 100644 --- a/spec/lib/gitlab/tree_summary_spec.rb +++ b/spec/lib/gitlab/tree_summary_spec.rb @@ -129,6 +129,17 @@ describe Gitlab::TreeSummary do expect(commits).to satisfy_one { |c| c.id == whitespace_commit_sha } end end + + context 'in a subdirectory with non-ASCII filenames' do + let(:path) { 'encoding' } + + it 'returns commits for entries in the subdirectory' do + entry = entries.find { |x| x[:file_name] == 'テスト.txt' } + + expect(entry).to be_a(Hash) + expect(entry).to include(:commit) + end + end end describe '#more?' do diff --git a/spec/lib/marginalia_spec.rb b/spec/lib/marginalia_spec.rb index db428bb65c4..d4b84c5cdc4 100644 --- a/spec/lib/marginalia_spec.rb +++ b/spec/lib/marginalia_spec.rb @@ -59,7 +59,6 @@ describe 'Marginalia spec' do "application" => "test", "controller" => "marginalia_test", "action" => "first_user", - "line" => "/spec/support/helpers/query_recorder.rb", "correlation_id" => correlation_id } end @@ -116,7 +115,6 @@ describe 'Marginalia spec' do { "application" => "sidekiq", "job_class" => "MarginaliaTestJob", - "line" => "/spec/support/sidekiq_middleware.rb", "correlation_id" => sidekiq_job['correlation_id'], "jid" => sidekiq_job['jid'] } @@ -145,7 +143,6 @@ describe 'Marginalia spec' do let(:component_map) do { "application" => "sidekiq", - "line" => "/lib/gitlab/i18n.rb", "jid" => delivery_job.job_id, "job_class" => delivery_job.arguments.first } diff --git a/spec/lib/sentry/client/issue_spec.rb b/spec/lib/sentry/client/issue_spec.rb index 2762c5b5cb9..d35e4b83d7f 100644 --- a/spec/lib/sentry/client/issue_spec.rb +++ b/spec/lib/sentry/client/issue_spec.rb @@ -49,7 +49,7 @@ describe Sentry::Client::Issue do it_behaves_like 'calls sentry api' it_behaves_like 'issues have correct return type', Gitlab::ErrorTracking::Error - it_behaves_like 'issues have correct length', 1 + it_behaves_like 'issues have correct length', 2 shared_examples 'has correct external_url' do context 'external_url' do @@ -184,7 +184,7 @@ describe Sentry::Client::Issue do it_behaves_like 'calls sentry api' it_behaves_like 'issues have correct return type', Gitlab::ErrorTracking::Error - it_behaves_like 'issues have correct length', 1 + it_behaves_like 'issues have correct length', 2 end context 'when cursor is present' do @@ -194,7 +194,7 @@ describe Sentry::Client::Issue do it_behaves_like 'calls sentry api' it_behaves_like 'issues have correct return type', Gitlab::ErrorTracking::Error - it_behaves_like 'issues have correct length', 1 + it_behaves_like 'issues have correct length', 2 end end diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb index 67d8284bebe..6cef81d6e44 100644 --- a/spec/models/broadcast_message_spec.rb +++ b/spec/models/broadcast_message_spec.rb @@ -65,6 +65,17 @@ describe BroadcastMessage do end end + it 'expires the value if a broadcast message has ended', :request_store do + message = create(:broadcast_message, broadcast_type: broadcast_type, ends_at: Time.now.utc + 1.day) + + expect(subject.call).to match_array([message]) + expect(described_class.cache).to receive(:expire).and_call_original + + Timecop.travel(1.week) do + 2.times { expect(subject.call).to be_empty } + end + end + it 'does not create new records' do create(:broadcast_message, broadcast_type: broadcast_type) diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 00ffc3cae54..a8cdebd2b9c 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2366,7 +2366,7 @@ describe Repository do end end - describe '#tree' do + shared_examples '#tree' do context 'using a non-existing repository' do before do allow(repository).to receive(:head_commit).and_return(nil) @@ -2384,10 +2384,17 @@ describe Repository do context 'using an existing repository' do it 'returns a Tree' do expect(repository.tree(:head)).to be_an_instance_of(Tree) + expect(repository.tree('v1.1.1')).to be_an_instance_of(Tree) end end end + it_behaves_like '#tree' + + describe '#tree? with Rugged enabled', :enable_rugged do + it_behaves_like '#tree' + end + describe '#size' do context 'with a non-existing repository' do it 'returns 0' do diff --git a/spec/support_specs/helpers/active_record/query_recorder_spec.rb b/spec/support_specs/helpers/active_record/query_recorder_spec.rb index 48069c6a766..0827ce37b07 100644 --- a/spec/support_specs/helpers/active_record/query_recorder_spec.rb +++ b/spec/support_specs/helpers/active_record/query_recorder_spec.rb @@ -14,9 +14,6 @@ describe ActiveRecord::QueryRecorder do TestQueries.first end - # Test first_only flag works as expected - expect(control.find_query(/.*query_recorder_spec.rb.*/, 0, first_only: true)) - .to eq(control.find_query(/.*query_recorder_spec.rb.*/, 0).first) # Check #find_query expect(control.find_query(/.*/, 0).size) .to eq(control.data.keys.size) @@ -32,9 +29,7 @@ describe ActiveRecord::QueryRecorder do # Ensure memoization value match the raw value above expect(control.count).to eq(control.log.size) # Ensure we have only two sources of queries - expect(control.data.keys.size).to eq(2) - # Ensure we detect only queries from this file - expect(control.data.keys.find_all { |i| i.match(/query_recorder_spec.rb/) }.count).to eq(2) + expect(control.data.keys.size).to eq(1) end end end diff --git a/spec/uploaders/import_export_uploader_spec.rb b/spec/uploaders/import_export_uploader_spec.rb index 7e8937ff5a6..33cab911f86 100644 --- a/spec/uploaders/import_export_uploader_spec.rb +++ b/spec/uploaders/import_export_uploader_spec.rb @@ -51,4 +51,10 @@ describe ImportExportUploader do end end end + + describe '.workhorse_local_upload_path' do + it 'returns path that includes uploads dir' do + expect(described_class.workhorse_local_upload_path).to end_with('/uploads/tmp/uploads') + end + end end |