diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-28 18:09:35 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-04-28 18:09:35 +0300 |
commit | 42d13aebd3c47671337d871e8b349385dade5252 (patch) | |
tree | c15b971738677229f079feab81821611f92ad6c9 | |
parent | 0805030d634b48c8a44308330fe0d99ba8434f46 (diff) |
Add latest changes from gitlab-org/gitlab@master
96 files changed, 1544 insertions, 146 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 096a598100e..f272dd674ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,17 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 13.11.2 (2021-04-27) + +### Security (5 changes) + +- Prevent tokens with only read_api scope from executing mutations. +- Do not allow deploy tokens in the dependency proxy authentication service. +- Disable keyset pagination for branches by default. +- Bump Carrierwave gem to v1.3.2. +- Restrict setting system_note_timestamp to owners. + + ## 13.11.1 (2021-04-22) ### Changed (1 change) @@ -819,6 +830,18 @@ entry. - Externalize strings in labels/new.html.haml. (nuwe1) +## 13.10.4 (2021-04-27) + +### Security (6 changes) + +- Prevent tokens with only read_api scope from executing mutations. +- Update mermaid to version 8.9.2. +- Do not allow deploy tokens in the dependency proxy authentication service. +- Disable keyset pagination for branches by default. +- Bump Carrierwave gem to v1.3.2. +- Restrict setting system_note_timestamp to owners. + + ## 13.10.3 (2021-04-13) ### Security (3 changes) @@ -1388,6 +1411,18 @@ entry. - Convert mattermost alert to pajamas. !56556 +## 13.9.7 (2021-04-27) + +### Security (6 changes) + +- Prevent tokens with only read_api scope from executing mutations. +- Update mermaid to version 8.9.2. +- Do not allow deploy tokens in the dependency proxy authentication service. +- Disable keyset pagination for branches by default. +- Bump Carrierwave gem to v1.3.2. +- Restrict setting system_note_timestamp to owners. + + ## 13.9.6 (2021-04-13) ### Security (2 changes) diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index a68349bf139..2d885695fcf 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -67520d7b374eea77f7412600271a90193bdd2e8a +34b2ac334944beb72e5000d9e90e5b7bdd4778fc diff --git a/Gemfile.lock b/Gemfile.lock index 989d812228f..0c2dc260970 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -173,10 +173,11 @@ GEM capybara-screenshot (1.0.22) capybara (>= 1.0, < 4) launchy - carrierwave (1.3.1) + carrierwave (1.3.2) activemodel (>= 4.0.0) activesupport (>= 4.0.0) mime-types (>= 1.16) + ssrf_filter (~> 1.0) cbor (0.5.9.6) character_set (1.4.0) charlock_holmes (0.7.7) @@ -1222,6 +1223,7 @@ GEM sprockets (>= 3.0.0) sqlite3 (1.3.13) sshkey (2.0.0) + ssrf_filter (1.0.7) stackprof (0.2.15) state_machines (0.5.0) state_machines-activemodel (0.8.0) diff --git a/app/assets/javascripts/batch_comments/components/drafts_count.vue b/app/assets/javascripts/batch_comments/components/drafts_count.vue index 5e110b101eb..61718b766d8 100644 --- a/app/assets/javascripts/batch_comments/components/drafts_count.vue +++ b/app/assets/javascripts/batch_comments/components/drafts_count.vue @@ -12,7 +12,7 @@ export default { }; </script> <template> - <gl-badge size="sm" variant="success"> + <gl-badge size="sm" variant="info" class="gl-ml-2"> {{ draftsCount }} <span class="sr-only"> {{ n__('draft', 'drafts', draftsCount) }} </span> </gl-badge> diff --git a/app/assets/javascripts/batch_comments/components/publish_button.vue b/app/assets/javascripts/batch_comments/components/publish_button.vue index 2a7be605003..d4fc4ad744a 100644 --- a/app/assets/javascripts/batch_comments/components/publish_button.vue +++ b/app/assets/javascripts/batch_comments/components/publish_button.vue @@ -22,7 +22,7 @@ export default { variant: { type: String, required: false, - default: 'success', + default: 'confirm', }, }, computed: { diff --git a/app/assets/javascripts/behaviors/markdown/render_mermaid.js b/app/assets/javascripts/behaviors/markdown/render_mermaid.js index 5b5148a850b..f5b2d266c18 100644 --- a/app/assets/javascripts/behaviors/markdown/render_mermaid.js +++ b/app/assets/javascripts/behaviors/markdown/render_mermaid.js @@ -1,5 +1,5 @@ import $ from 'jquery'; -import { once } from 'lodash'; +import { once, countBy } from 'lodash'; import { deprecatedCreateFlash as flash } from '~/flash'; import { darkModeEnabled } from '~/lib/utils/color_utils'; import { __, sprintf } from '~/locale'; @@ -22,6 +22,8 @@ import { __, sprintf } from '~/locale'; const MAX_CHAR_LIMIT = 2000; // Max # of mermaid blocks that can be rendered in a page. const MAX_MERMAID_BLOCK_LIMIT = 50; +// Max # of `&` allowed in Chaining of links syntax +const MAX_CHAINING_OF_LINKS_LIMIT = 30; // Keep a map of mermaid blocks we've already rendered. const elsProcessingMap = new WeakMap(); let renderedMermaidBlocks = 0; @@ -64,6 +66,18 @@ function importMermaidModule() { }); } +function shouldLazyLoadMermaidBlock(source) { + /** + * If source contains `&`, which means that it might + * contain Chaining of links a new syntax in Mermaid. + */ + if (countBy(source)['&'] > MAX_CHAINING_OF_LINKS_LIMIT) { + return true; + } + + return false; +} + function fixElementSource(el) { // Mermaid doesn't like `<br />` tags, so collapse all like tags into `<br>`, which is parsed correctly. const source = el.textContent.replace(/<br\s*\/>/g, '<br>'); @@ -128,7 +142,8 @@ function renderMermaids($els) { if ( (source && source.length > MAX_CHAR_LIMIT) || renderedChars > MAX_CHAR_LIMIT || - renderedMermaidBlocks >= MAX_MERMAID_BLOCK_LIMIT + renderedMermaidBlocks >= MAX_MERMAID_BLOCK_LIMIT || + shouldLazyLoadMermaidBlock(source) ) { const html = ` <div class="alert gl-alert gl-alert-warning alert-dismissible lazy-render-mermaid-container js-lazy-render-mermaid-container fade show" role="alert"> diff --git a/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue b/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue index fb25d3618ab..336ce714a05 100644 --- a/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue +++ b/app/assets/javascripts/design_management/components/design_notes/design_reply_form.vue @@ -115,12 +115,13 @@ export default { </template> </markdown-field> <slot name="resolve-checkbox"></slot> - <div class="note-form-actions gl-display-flex gl-justify-content-space-between"> + <div class="note-form-actions gl-display-flex"> <gl-button ref="submitButton" :disabled="!hasValue || isSaving" + class="gl-mr-3 gl-w-auto!" category="primary" - variant="success" + variant="confirm" type="submit" data-track-event="click_button" data-qa-selector="save_comment_button" @@ -128,9 +129,14 @@ export default { > {{ buttonText }} </gl-button> - <gl-button ref="cancelButton" variant="default" category="primary" @click="cancelComment">{{ - __('Cancel') - }}</gl-button> + <gl-button + ref="cancelButton" + class="gl-w-auto!" + variant="default" + category="primary" + @click="cancelComment" + >{{ __('Cancel') }}</gl-button + > </div> <gl-modal ref="cancelCommentModal" diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index 7c610968209..66157dfeaba 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -3,6 +3,7 @@ import { GlLoadingIcon, GlPagination, GlSprintf } from '@gitlab/ui'; import { GlBreakpointInstance as bp } from '@gitlab/ui/dist/utils'; import Mousetrap from 'mousetrap'; import { mapState, mapGetters, mapActions } from 'vuex'; +import api from '~/api'; import { keysFor, MR_PREVIOUS_FILE_IN_DIFF, @@ -30,6 +31,15 @@ import { ALERT_OVERFLOW_HIDDEN, ALERT_MERGE_CONFLICT, ALERT_COLLAPSED_FILES, + INLINE_DIFF_VIEW_TYPE, + TRACKING_DIFF_VIEW_INLINE, + TRACKING_DIFF_VIEW_PARALLEL, + TRACKING_FILE_BROWSER_TREE, + TRACKING_FILE_BROWSER_LIST, + TRACKING_WHITESPACE_SHOW, + TRACKING_WHITESPACE_HIDE, + TRACKING_SINGLE_FILE_MODE, + TRACKING_MULTIPLE_FILES_MODE, } from '../constants'; import { reviewStatuses } from '../utils/file_reviews'; @@ -183,6 +193,8 @@ export default { 'hasConflicts', 'viewDiffsFileByFile', 'mrReviews', + 'renderTreeList', + 'showWhitespace', ]), ...mapGetters('diffs', ['whichCollapsedTypes', 'isParallelView', 'currentDiffIndex']), ...mapGetters('batchComments', ['draftsCount']), @@ -305,6 +317,32 @@ export default { if (id && id.indexOf('#note') !== 0) { this.setHighlightedRow(id.split('diff-content').pop().slice(1)); } + + if (window.gon?.features?.diffSettingsUsageData) { + if (this.renderTreeList) { + api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_TREE); + } else { + api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_LIST); + } + + if (this.diffViewType === INLINE_DIFF_VIEW_TYPE) { + api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_INLINE); + } else { + api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_PARALLEL); + } + + if (this.showWhitespace) { + api.trackRedisHllUserEvent(TRACKING_WHITESPACE_SHOW); + } else { + api.trackRedisHllUserEvent(TRACKING_WHITESPACE_HIDE); + } + + if (this.viewDiffsFileByFile) { + api.trackRedisHllUserEvent(TRACKING_SINGLE_FILE_MODE); + } else { + api.trackRedisHllUserEvent(TRACKING_MULTIPLE_FILES_MODE); + } + } }, beforeCreate() { diffsApp.instrument(); diff --git a/app/assets/javascripts/diffs/constants.js b/app/assets/javascripts/diffs/constants.js index 0163f508fea..f0e15983336 100644 --- a/app/assets/javascripts/diffs/constants.js +++ b/app/assets/javascripts/diffs/constants.js @@ -114,3 +114,20 @@ export const CONFLICT_THEIR = 'conflict_their'; export const CONFLICT_MARKER = 'conflict_marker'; export const CONFLICT_MARKER_OUR = 'conflict_marker_our'; export const CONFLICT_MARKER_THEIR = 'conflict_marker_their'; + +// Tracking events +export const TRACKING_CLICK_DIFF_VIEW_SETTING = 'i_code_review_click_diff_view_setting'; +export const TRACKING_DIFF_VIEW_INLINE = 'i_code_review_diff_view_inline'; +export const TRACKING_DIFF_VIEW_PARALLEL = 'i_code_review_diff_view_parallel'; + +export const TRACKING_CLICK_FILE_BROWSER_SETTING = 'i_code_review_click_file_browser_setting'; +export const TRACKING_FILE_BROWSER_TREE = 'i_code_review_file_browser_tree_view'; +export const TRACKING_FILE_BROWSER_LIST = 'i_code_review_file_browser_list_view'; + +export const TRACKING_CLICK_WHITESPACE_SETTING = 'i_code_review_click_whitespace_setting'; +export const TRACKING_WHITESPACE_SHOW = 'i_code_review_diff_show_whitespace'; +export const TRACKING_WHITESPACE_HIDE = 'i_code_review_diff_hide_whitespace'; + +export const TRACKING_CLICK_SINGLE_FILE_SETTING = 'i_code_review_click_single_file_mode_setting'; +export const TRACKING_SINGLE_FILE_MODE = 'i_code_review_diff_single_file'; +export const TRACKING_MULTIPLE_FILES_MODE = 'i_code_review_diff_multiple_files'; diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 428faf693b0..d0730e18228 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -1,5 +1,6 @@ import Cookies from 'js-cookie'; import Vue from 'vue'; +import api from '~/api'; import { deprecatedCreateFlash as createFlash } from '~/flash'; import { diffViewerModes } from '~/ide/constants'; import axios from '~/lib/utils/axios_utils'; @@ -36,6 +37,18 @@ import { DIFF_VIEW_FILE_BY_FILE, DIFF_VIEW_ALL_FILES, DIFF_FILE_BY_FILE_COOKIE_NAME, + TRACKING_CLICK_DIFF_VIEW_SETTING, + TRACKING_DIFF_VIEW_INLINE, + TRACKING_DIFF_VIEW_PARALLEL, + TRACKING_CLICK_FILE_BROWSER_SETTING, + TRACKING_FILE_BROWSER_TREE, + TRACKING_FILE_BROWSER_LIST, + TRACKING_CLICK_WHITESPACE_SETTING, + TRACKING_WHITESPACE_SHOW, + TRACKING_WHITESPACE_HIDE, + TRACKING_CLICK_SINGLE_FILE_SETTING, + TRACKING_SINGLE_FILE_MODE, + TRACKING_MULTIPLE_FILES_MODE, } from '../constants'; import eventHub from '../event_hub'; import { isCollapsed } from '../utils/diff_file'; @@ -352,6 +365,11 @@ export const setInlineDiffViewType = ({ commit }) => { Cookies.set(DIFF_VIEW_COOKIE_NAME, INLINE_DIFF_VIEW_TYPE); const url = mergeUrlParams({ view: INLINE_DIFF_VIEW_TYPE }, window.location.href); historyPushState(url); + + if (window.gon?.features?.diffSettingsUsageData) { + api.trackRedisHllUserEvent(TRACKING_CLICK_DIFF_VIEW_SETTING); + api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_INLINE); + } }; export const setParallelDiffViewType = ({ commit }) => { @@ -360,6 +378,11 @@ export const setParallelDiffViewType = ({ commit }) => { Cookies.set(DIFF_VIEW_COOKIE_NAME, PARALLEL_DIFF_VIEW_TYPE); const url = mergeUrlParams({ view: PARALLEL_DIFF_VIEW_TYPE }, window.location.href); historyPushState(url); + + if (window.gon?.features?.diffSettingsUsageData) { + api.trackRedisHllUserEvent(TRACKING_CLICK_DIFF_VIEW_SETTING); + api.trackRedisHllUserEvent(TRACKING_DIFF_VIEW_PARALLEL); + } }; export const showCommentForm = ({ commit }, { lineCode, fileHash }) => { @@ -527,6 +550,16 @@ export const setRenderTreeList = ({ commit }, renderTreeList) => { commit(types.SET_RENDER_TREE_LIST, renderTreeList); localStorage.setItem(TREE_LIST_STORAGE_KEY, renderTreeList); + + if (window.gon?.features?.diffSettingsUsageData) { + api.trackRedisHllUserEvent(TRACKING_CLICK_FILE_BROWSER_SETTING); + + if (renderTreeList) { + api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_TREE); + } else { + api.trackRedisHllUserEvent(TRACKING_FILE_BROWSER_LIST); + } + } }; export const setShowWhitespace = ({ commit }, { showWhitespace, pushState = false }) => { @@ -540,6 +573,16 @@ export const setShowWhitespace = ({ commit }, { showWhitespace, pushState = fals } notesEventHub.$emit('refetchDiffData'); + + if (window.gon?.features?.diffSettingsUsageData) { + api.trackRedisHllUserEvent(TRACKING_CLICK_WHITESPACE_SETTING); + + if (showWhitespace) { + api.trackRedisHllUserEvent(TRACKING_WHITESPACE_SHOW); + } else { + api.trackRedisHllUserEvent(TRACKING_WHITESPACE_HIDE); + } + } }; export const toggleFileFinder = ({ commit }, visible) => { @@ -754,6 +797,16 @@ export const setFileByFile = ({ state, commit }, { fileByFile }) => { commit(types.SET_FILE_BY_FILE, fileByFile); Cookies.set(DIFF_FILE_BY_FILE_COOKIE_NAME, fileViewMode); + if (window.gon?.features?.diffSettingsUsageData) { + api.trackRedisHllUserEvent(TRACKING_CLICK_SINGLE_FILE_SETTING); + + if (fileByFile) { + api.trackRedisHllUserEvent(TRACKING_SINGLE_FILE_MODE); + } else { + api.trackRedisHllUserEvent(TRACKING_MULTIPLE_FILES_MODE); + } + } + return axios .put(state.endpointUpdateUser, { view_diffs_file_by_file: fileByFile, diff --git a/app/assets/javascripts/notes/components/note_form.vue b/app/assets/javascripts/notes/components/note_form.vue index a70bac94b71..5b40f4f86f5 100644 --- a/app/assets/javascripts/notes/components/note_form.vue +++ b/app/assets/javascripts/notes/components/note_form.vue @@ -362,7 +362,7 @@ export default { </template> </markdown-field> </comment-field-layout> - <div class="note-form-actions clearfix"> + <div class="note-form-actions"> <template v-if="showBatchCommentsActions"> <p v-if="showResolveDiscussionToggle"> <label> @@ -386,12 +386,12 @@ export default { </template> </label> </p> - <div class="gl-display-sm-flex gl-flex-wrap"> + <div class="gl-display-flex gl-flex-wrap gl-mb-n3"> <gl-button :disabled="isDisabled" category="primary" variant="confirm" - class="gl-mr-3" + class="gl-sm-mr-3 gl-mb-3" data-qa-selector="start_review_button" @click="handleAddToReview" > @@ -401,15 +401,15 @@ export default { <gl-button :disabled="isDisabled" category="secondary" - variant="default" + variant="confirm" data-qa-selector="comment_now_button" - class="gl-mr-3 js-comment-button" + class="gl-sm-mr-3 gl-mb-3 js-comment-button" @click="handleUpdate()" > {{ __('Add comment now') }} </gl-button> <gl-button - class="note-edit-cancel js-close-discussion-note-form" + class="note-edit-cancel gl-mb-3 js-close-discussion-note-form" category="secondary" variant="default" data-testid="cancelBatchCommentsEnabled" diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 59768f4cda8..c025d8569a7 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -369,10 +369,6 @@ table { .btn { float: none; width: 100%; - - &:not(:last-child) { - margin-bottom: 10px; - } } } } diff --git a/app/assets/stylesheets/utilities.scss b/app/assets/stylesheets/utilities.scss index 024162eba3e..f9e9a7a99b7 100644 --- a/app/assets/stylesheets/utilities.scss +++ b/app/assets/stylesheets/utilities.scss @@ -172,3 +172,13 @@ width: 50%; } } + +.gl-sm-mr-3 { + @include media-breakpoint-up(sm) { + margin-right: $gl-spacing-scale-3; + } +} + +.gl-mb-n3 { + margin-bottom: -$gl-spacing-scale-3; +} diff --git a/app/controllers/concerns/sessionless_authentication.rb b/app/controllers/concerns/sessionless_authentication.rb index 882fef7a342..3c8a683439a 100644 --- a/app/controllers/concerns/sessionless_authentication.rb +++ b/app/controllers/concerns/sessionless_authentication.rb @@ -7,11 +7,15 @@ module SessionlessAuthentication # This filter handles personal access tokens, atom requests with rss tokens, and static object tokens def authenticate_sessionless_user!(request_format) - user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user(request_format) + user = request_authenticator.find_sessionless_user(request_format) sessionless_sign_in(user) if user end + def request_authenticator + @request_authenticator ||= Gitlab::Auth::RequestAuthenticator.new(request) + end + def sessionless_user? current_user && !session.key?('warden.user.user.key') end diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb index b7daff04373..725d8b62c77 100644 --- a/app/controllers/graphql_controller.rb +++ b/app/controllers/graphql_controller.rb @@ -112,7 +112,13 @@ class GraphqlController < ApplicationController # When modifying the context, also update GraphqlChannel#context if needed # so that we have similar context when executing queries, mutations, and subscriptions def context - @context ||= { current_user: current_user, is_sessionless_user: !!sessionless_user?, request: request } + api_user = !!sessionless_user? + @context ||= { + current_user: current_user, + is_sessionless_user: api_user, + request: request, + scope_validator: ::Gitlab::Auth::ScopeValidator.new(api_user, request_authenticator) + } end def build_variables(variable_info) diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index f522dffdf3e..5006aa75ce5 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -185,7 +185,7 @@ class Projects::BranchesController < Projects::ApplicationController # Here we get one more branch to indicate if there are more data we're not showing limit = @overview_max_branches + 1 - if Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: true) + if Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: :yaml) @active_branches = BranchesFinder.new(@repository, { per_page: limit, sort: sort_value_recently_updated }) .execute(gitaly_pagination: true).select(&:active?) diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 4e409b5f28f..a31437288b9 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -45,6 +45,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo # Usage data feature flags push_frontend_feature_flag(:users_expanding_widgets_usage_data, @project, default_enabled: :yaml) + push_frontend_feature_flag(:diff_settings_usage_data, default_enabled: :yaml) record_experiment_user(:invite_members_version_b) diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb index a6ecd835527..430ff212977 100644 --- a/app/finders/group_members_finder.rb +++ b/app/finders/group_members_finder.rb @@ -4,6 +4,12 @@ class GroupMembersFinder < UnionFinder RELATIONS = %i(direct inherited descendants).freeze DEFAULT_RELATIONS = %i(direct inherited).freeze + RELATIONS_DESCRIPTIONS = { + direct: 'Members in the group itself', + inherited: "Members in the group's ancestor groups", + descendants: "Members in the group's subgroups" + }.freeze + include CreatedAtFilter # Params can be any of the following: diff --git a/app/graphql/mutations/base_mutation.rb b/app/graphql/mutations/base_mutation.rb index 1f18a37fcb9..da658e1f108 100644 --- a/app/graphql/mutations/base_mutation.rb +++ b/app/graphql/mutations/base_mutation.rb @@ -44,9 +44,18 @@ module Mutations end end + def self.authorizes_object? + true + end + def self.authorized?(object, context) - # we never provide an object to mutations, but we do need to have a user. - context[:current_user].present? && !context[:current_user].blocked? + auth = ::Gitlab::Graphql::Authorize::ObjectAuthorization.new(:execute_graphql_mutation, :api) + + return true if auth.ok?(:global, context[:current_user], + scope_validator: context[:scope_validator]) + + # in our mutations we raise, rather than returning a null value. + raise_resource_not_available_error! end # See: AuthorizeResource#authorized_resource? diff --git a/app/graphql/types/group_member_relation_enum.rb b/app/graphql/types/group_member_relation_enum.rb index aa2e73d4944..ce22410a249 100644 --- a/app/graphql/types/group_member_relation_enum.rb +++ b/app/graphql/types/group_member_relation_enum.rb @@ -6,7 +6,7 @@ module Types description 'Group member relation' ::GroupMembersFinder::RELATIONS.each do |member_relation| - value member_relation.to_s.upcase, value: member_relation, description: "#{member_relation.to_s.titleize} members" + value member_relation.to_s.upcase, value: member_relation, description: ::GroupMembersFinder::RELATIONS_DESCRIPTIONS[member_relation] end end end diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index add6e1eaf6f..6fc2dfb597b 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -4,8 +4,8 @@ module PreferencesHelper def layout_choices [ - ['Fixed', :fixed], - ['Fluid', :fluid] + ['Fixed', :fixed], + ['Fluid', :fluid] ] end @@ -76,7 +76,7 @@ module PreferencesHelper def language_choices options_for_select( - Gitlab::I18n.selectable_locales.map(&:reverse).sort, + selectable_locales_with_translation_level.sort, current_user.preferred_language ) end @@ -107,6 +107,18 @@ module PreferencesHelper def default_first_day_of_week first_day_of_week_choices.rassoc(Gitlab::CurrentSettings.first_day_of_week).first end + + def selectable_locales_with_translation_level + Gitlab::I18n.selectable_locales.map do |code, language| + [ + s_("i18n|%{language} (%{percent_translated}%% translated)") % { + language: language, + percent_translated: Gitlab::I18n.percentage_translated_for(code) + }, + code + ] + end + end end PreferencesHelper.prepend_if_ee('EE::PreferencesHelper') diff --git a/app/models/user.rb b/app/models/user.rb index 92cbf863d63..2a1a910effd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -431,7 +431,7 @@ class User < ApplicationRecord def preferred_language read_attribute('preferred_language') || - I18n.default_locale.to_s.presence_in(Gitlab::I18n::AVAILABLE_LANGUAGES.keys) || + I18n.default_locale.to_s.presence_in(Gitlab::I18n.available_locales) || 'en' end diff --git a/app/policies/global_policy.rb b/app/policies/global_policy.rb index 5ee34ebbb2f..d16c4734b2c 100644 --- a/app/policies/global_policy.rb +++ b/app/policies/global_policy.rb @@ -23,6 +23,7 @@ class GlobalPolicy < BasePolicy prevent :receive_notifications prevent :use_quick_actions prevent :create_group + prevent :execute_graphql_mutation end rule { default }.policy do @@ -32,6 +33,7 @@ class GlobalPolicy < BasePolicy enable :receive_notifications enable :use_quick_actions enable :use_slash_commands + enable :execute_graphql_mutation end rule { inactive }.policy do @@ -48,6 +50,8 @@ class GlobalPolicy < BasePolicy prevent :use_slash_commands end + rule { ~can?(:access_api) }.prevent :execute_graphql_mutation + rule { blocked | (internal & ~migration_bot & ~security_bot) }.policy do prevent :access_git end diff --git a/app/services/auth/dependency_proxy_authentication_service.rb b/app/services/auth/dependency_proxy_authentication_service.rb index 1b8c16b7c79..fab42e0ebb6 100644 --- a/app/services/auth/dependency_proxy_authentication_service.rb +++ b/app/services/auth/dependency_proxy_authentication_service.rb @@ -8,7 +8,10 @@ module Auth def execute(authentication_abilities:) return error('dependency proxy not enabled', 404) unless ::Gitlab.config.dependency_proxy.enabled - return error('access forbidden', 403) unless current_user + + # Because app/controllers/concerns/dependency_proxy/auth.rb consumes this + # JWT only as `User.find`, we currently only allow User (not DeployToken, etc) + return error('access forbidden', 403) unless current_user.is_a?(User) { token: authorized_token.encoded } end diff --git a/app/services/issues/base_service.rb b/app/services/issues/base_service.rb index 87615d1b4f2..07e4a10708e 100644 --- a/app/services/issues/base_service.rb +++ b/app/services/issues/base_service.rb @@ -34,7 +34,7 @@ module Issues private - def filter_params(merge_request) + def filter_params(issue) super moved_issue = params.delete(:moved_issue) @@ -44,6 +44,8 @@ module Issues params.delete(:iid) unless current_user.can?(:set_issue_iid, project) params.delete(:created_at) unless moved_issue || current_user.can?(:set_issue_created_at, project) params.delete(:updated_at) unless moved_issue || current_user.can?(:set_issue_updated_at, project) + + issue.system_note_timestamp = params[:created_at] || params[:updated_at] end def create_assignee_note(issue, old_assignees) diff --git a/app/services/projects/branches_by_mode_service.rb b/app/services/projects/branches_by_mode_service.rb index dbdcef066f4..090671cc79a 100644 --- a/app/services/projects/branches_by_mode_service.rb +++ b/app/services/projects/branches_by_mode_service.rb @@ -37,7 +37,7 @@ class Projects::BranchesByModeService def use_gitaly_pagination? return false if params[:page].present? || params[:search].present? - Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: true) + Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: :yaml) end def fetch_branches_via_offset_pagination diff --git a/app/views/import/bitbucket_server/new.html.haml b/app/views/import/bitbucket_server/new.html.haml index 308065da90a..8a3fe1a816c 100644 --- a/app/views/import/bitbucket_server/new.html.haml +++ b/app/views/import/bitbucket_server/new.html.haml @@ -15,14 +15,14 @@ .form-group.row = label_tag :bitbucket_server_url, 'Bitbucket Server URL', class: 'col-form-label col-md-2' .col-md-4 - = text_field_tag :bitbucket_server_url, '', class: 'form-control gl-mr-3', placeholder: _('https://your-bitbucket-server'), size: 40 + = text_field_tag :bitbucket_server_url, '', class: 'form-control gl-form-input gl-mr-3', placeholder: _('https://your-bitbucket-server'), size: 40 .form-group.row = label_tag :bitbucket_server_url, 'Username', class: 'col-form-label col-md-2' .col-md-4 - = text_field_tag :bitbucket_server_username, '', class: 'form-control gl-mr-3', placeholder: _('username'), size: 40 + = text_field_tag :bitbucket_server_username, '', class: 'form-control gl-form-input gl-mr-3', placeholder: _('username'), size: 40 .form-group.row = label_tag :personal_access_token, 'Password/Personal Access Token', class: 'col-form-label col-md-2' .col-md-4 - = password_field_tag :personal_access_token, '', class: 'form-control gl-mr-3', placeholder: _('Personal Access Token'), size: 40 + = password_field_tag :personal_access_token, '', class: 'form-control gl-form-input gl-mr-3', placeholder: _('Personal Access Token'), size: 40 .form-actions = submit_tag _('List your Bitbucket Server repositories'), class: 'gl-button btn btn-confirm' diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml index c0abac0a633..ab836174024 100644 --- a/app/views/import/fogbugz/new.html.haml +++ b/app/views/import/fogbugz/new.html.haml @@ -12,14 +12,14 @@ .form-group.row = label_tag :uri, _('FogBugz URL'), class: 'col-form-label col-md-2' .col-md-4 - = text_field_tag :uri, nil, placeholder: 'https://mycompany.fogbugz.com', class: 'form-control' + = text_field_tag :uri, nil, placeholder: 'https://mycompany.fogbugz.com', class: 'form-control gl-form-input' .form-group.row = label_tag :email, _('FogBugz Email'), class: 'col-form-label col-md-2' .col-md-4 - = text_field_tag :email, nil, class: 'form-control' + = text_field_tag :email, nil, class: 'form-control gl-form-input' .form-group.row = label_tag :password, _('FogBugz Password'), class: 'col-form-label col-md-2' .col-md-4 - = password_field_tag :password, nil, class: 'form-control' + = password_field_tag :password, nil, class: 'form-control gl-form-input' .form-actions = submit_tag _('Continue to the next step'), class: 'gl-button btn btn-confirm' diff --git a/app/views/import/gitea/new.html.haml b/app/views/import/gitea/new.html.haml index 285d2fb23a3..27786806d17 100644 --- a/app/views/import/gitea/new.html.haml +++ b/app/views/import/gitea/new.html.haml @@ -13,10 +13,10 @@ .form-group.row = label_tag :gitea_host_url, _('Gitea Host URL'), class: 'col-form-label col-sm-2' .col-sm-4 - = text_field_tag :gitea_host_url, nil, placeholder: 'https://gitea.com', class: 'form-control' + = text_field_tag :gitea_host_url, nil, placeholder: 'https://gitea.com', class: 'form-control gl-form-input' .form-group.row = label_tag :personal_access_token, _('Personal Access Token'), class: 'col-form-label col-sm-2' .col-sm-4 - = text_field_tag :personal_access_token, nil, class: 'form-control' + = text_field_tag :personal_access_token, nil, class: 'form-control gl-form-input' .form-actions = submit_tag _('List Your Gitea Repositories'), class: 'gl-button btn btn-confirm' diff --git a/app/views/layouts/nav/sidebar/_project_menus.html.haml b/app/views/layouts/nav/sidebar/_project_menus.html.haml index 0507f6d4b16..631fc3f095d 100644 --- a/app/views/layouts/nav/sidebar/_project_menus.html.haml +++ b/app/views/layouts/nav/sidebar/_project_menus.html.haml @@ -1,22 +1,3 @@ -- if project_nav_tab? :merge_requests - = nav_link(controller: @project.issues_enabled? ? :merge_requests : [:merge_requests, :milestones]) do - = link_to project_merge_requests_path(@project), class: 'shortcuts-merge_requests', data: { qa_selector: 'merge_requests_link' } do - .nav-icon-container - = sprite_icon('git-merge') - %span.nav-item-name#js-onboarding-mr-link - = _('Merge requests') - %span.badge.badge-pill.count.merge_counter.js-merge-counter - = number_with_delimiter(@project.open_merge_requests_count) - %ul.sidebar-sub-level-items.is-fly-out-only - = nav_link(controller: :merge_requests, html_options: { class: "fly-out-top-item" } ) do - = link_to project_merge_requests_path(@project) do - %strong.fly-out-top-item-name - = _('Merge requests') - %span.badge.badge-pill.count.merge_counter.js-merge-counter.fly-out-badge - = number_with_delimiter(@project.open_merge_requests_count) - -= render_if_exists "layouts/nav/requirements_link", project: @project - - if project_nav_tab? :pipelines = nav_link(controller: [:pipelines, :builds, :jobs, :pipeline_schedules, :artifacts, :test_cases, :pipeline_editor], unless: -> { current_path?('projects/pipelines#charts') }) do = link_to project_pipelines_path(@project), class: 'shortcuts-pipelines qa-link-pipelines rspec-link-pipelines', data: { qa_selector: 'ci_cd_link' } do diff --git a/app/views/projects/mirrors/_authentication_method.html.haml b/app/views/projects/mirrors/_authentication_method.html.haml index 94f8703657b..5f31ec4087e 100644 --- a/app/views/projects/mirrors/_authentication_method.html.haml +++ b/app/views/projects/mirrors/_authentication_method.html.haml @@ -13,4 +13,4 @@ .form-group .well-password-auth.collapse.js-well-password-auth = f.label :password, _("Password"), class: "label-bold" - = f.password_field :password, value: mirror.password, class: 'form-control gl-form-input qa-password', autocomplete: 'new-password' + = f.password_field :password, class: 'form-control gl-form-input qa-password', autocomplete: 'new-password' diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml index 36d8aab6d53..65e02341936 100644 --- a/app/views/shared/_import_form.html.haml +++ b/app/views/shared/_import_form.html.haml @@ -7,20 +7,20 @@ %span = _('Git repository URL') = f.text_field :import_url, value: import_url.sanitized_url, - autocomplete: 'off', class: 'form-control', placeholder: 'https://gitlab.company.com/group/project.git', required: true + autocomplete: 'off', class: 'form-control gl-form-input', placeholder: 'https://gitlab.company.com/group/project.git', required: true .row .form-group.col-md-6 = f.label :import_url_user, class: 'label-bold' do %span = _('Username (optional)') - = f.text_field :import_url_user, value: import_url.user, class: 'form-control', required: false, autocomplete: 'new-password' + = f.text_field :import_url_user, value: import_url.user, class: 'form-control gl-form-input', required: false, autocomplete: 'new-password' .form-group.col-md-6 = f.label :import_url_password, class: 'label-bold' do %span = _('Password (optional)') - = f.password_field :import_url_password, class: 'form-control', required: false, autocomplete: 'new-password' + = f.password_field :import_url_password, class: 'form-control gl-form-input', required: false, autocomplete: 'new-password' .info-well.prepend-top-20 .well-segment diff --git a/changelogs/unreleased/gl-form-bitbucket-import.yml b/changelogs/unreleased/gl-form-bitbucket-import.yml new file mode 100644 index 00000000000..e0058c058f8 --- /dev/null +++ b/changelogs/unreleased/gl-form-bitbucket-import.yml @@ -0,0 +1,5 @@ +--- +title: Add gl-form-input class for fields in bitbucket import page +merge_request: 58309 +author: Yogi (@yo) +type: changed diff --git a/changelogs/unreleased/gl-form-fogbugz-import.yml b/changelogs/unreleased/gl-form-fogbugz-import.yml new file mode 100644 index 00000000000..69d0f277a24 --- /dev/null +++ b/changelogs/unreleased/gl-form-fogbugz-import.yml @@ -0,0 +1,5 @@ +--- +title: Add gl-form-input class for fields in fogbugz import page +merge_request: 58312 +author: Yogi (@yo) +type: changed diff --git a/changelogs/unreleased/gl-form-gitea-import.yml b/changelogs/unreleased/gl-form-gitea-import.yml new file mode 100644 index 00000000000..b66aa41a8f9 --- /dev/null +++ b/changelogs/unreleased/gl-form-gitea-import.yml @@ -0,0 +1,5 @@ +--- +title: Add gl-form-input class for fields in gitea import page +merge_request: 58313 +author: Yogi (@yo) +type: changed diff --git a/changelogs/unreleased/gl-form-import.yml b/changelogs/unreleased/gl-form-import.yml new file mode 100644 index 00000000000..988ef53c4d4 --- /dev/null +++ b/changelogs/unreleased/gl-form-import.yml @@ -0,0 +1,5 @@ +--- +title: Add gl-form-input class for fields in import page +merge_request: 58316 +author: Yogi (@yo) +type: changed diff --git a/changelogs/unreleased/justin_ho-update-button-variant-to-confirm-on-merge-request-page.yml b/changelogs/unreleased/justin_ho-update-button-variant-to-confirm-on-merge-request-page.yml new file mode 100644 index 00000000000..d9ba6b8124a --- /dev/null +++ b/changelogs/unreleased/justin_ho-update-button-variant-to-confirm-on-merge-request-page.yml @@ -0,0 +1,5 @@ +--- +title: Update button variants to btn-confirm on MR page +merge_request: 60254 +author: +type: changed diff --git a/changelogs/unreleased/mermaid-chaining.yml b/changelogs/unreleased/mermaid-chaining.yml new file mode 100644 index 00000000000..589aed8a506 --- /dev/null +++ b/changelogs/unreleased/mermaid-chaining.yml @@ -0,0 +1,5 @@ +--- +title: Prevent DOS from Chaining in Mermaid +merge_request: 60382 +author: +type: security diff --git a/changelogs/unreleased/ph-327054-mrSettingsTracking.yml b/changelogs/unreleased/ph-327054-mrSettingsTracking.yml new file mode 100644 index 00000000000..a3cc1459680 --- /dev/null +++ b/changelogs/unreleased/ph-327054-mrSettingsTracking.yml @@ -0,0 +1,5 @@ +--- +title: Added tracking to diff view settings +merge_request: 59979 +author: +type: added diff --git a/changelogs/unreleased/trans-levels-master-patch-26507.yml b/changelogs/unreleased/trans-levels-master-patch-26507.yml new file mode 100644 index 00000000000..f56e33ed39d --- /dev/null +++ b/changelogs/unreleased/trans-levels-master-patch-26507.yml @@ -0,0 +1,5 @@ +--- +title: Add the translation level for each language in the user profile language selector +merge_request: 59420 +author: +type: changed diff --git a/config/feature_flags/development/branch_list_keyset_pagination.yml b/config/feature_flags/development/branch_list_keyset_pagination.yml index 23b573e5004..12200292058 100644 --- a/config/feature_flags/development/branch_list_keyset_pagination.yml +++ b/config/feature_flags/development/branch_list_keyset_pagination.yml @@ -5,4 +5,4 @@ rollout_issue_url: milestone: '13.2' type: development group: group::source code -default_enabled: true +default_enabled: false diff --git a/config/feature_flags/development/diff_settings_usage_data.yml b/config/feature_flags/development/diff_settings_usage_data.yml new file mode 100644 index 00000000000..e58552b51c2 --- /dev/null +++ b/config/feature_flags/development/diff_settings_usage_data.yml @@ -0,0 +1,8 @@ +--- +name: diff_settings_usage_data +introduced_by_url: +rollout_issue_url: +milestone: '13.11' +type: development +group: group::code review +default_enabled: true diff --git a/config/metrics/aggregates/code_review.yml b/config/metrics/aggregates/code_review.yml index a2e931bd217..e1f30777612 100644 --- a/config/metrics/aggregates/code_review.yml +++ b/config/metrics/aggregates/code_review.yml @@ -52,6 +52,17 @@ - 'i_code_review_user_reviewers_changed' - 'i_code_review_user_milestone_changed' - 'i_code_review_user_labels_changed' + - 'i_code_review_click_single_file_mode_setting' + - 'i_code_review_click_file_browser_setting' + - 'i_code_review_click_whitespace_setting' + - 'i_code_review_diff_view_inline' + - 'i_code_review_diff_view_parallel' + - 'i_code_review_file_browser_tree_view' + - 'i_code_review_file_browser_list_view' + - 'i_code_review_diff_show_whitespace' + - 'i_code_review_diff_hide_whitespace' + - 'i_code_review_diff_single_file' + - 'i_code_review_diff_multiple_files' - name: code_review_category_monthly_active_users operator: OR feature_flag: usage_data_code_review_aggregation @@ -96,6 +107,17 @@ - 'i_code_review_user_reviewers_changed' - 'i_code_review_user_milestone_changed' - 'i_code_review_user_labels_changed' + - 'i_code_review_click_single_file_mode_setting' + - 'i_code_review_click_file_browser_setting' + - 'i_code_review_click_whitespace_setting' + - 'i_code_review_diff_view_inline' + - 'i_code_review_diff_view_parallel' + - 'i_code_review_file_browser_tree_view' + - 'i_code_review_file_browser_list_view' + - 'i_code_review_diff_show_whitespace' + - 'i_code_review_diff_hide_whitespace' + - 'i_code_review_diff_single_file' + - 'i_code_review_diff_multiple_files' - name: code_review_extension_category_monthly_active_users operator: OR feature_flag: usage_data_code_review_aggregation diff --git a/config/metrics/counts_28d/20210421144352_i_code_review_click_single_file_mode_setting_monthly.yml b/config/metrics/counts_28d/20210421144352_i_code_review_click_single_file_mode_setting_monthly.yml new file mode 100644 index 00000000000..2bcd2a448ec --- /dev/null +++ b/config/metrics/counts_28d/20210421144352_i_code_review_click_single_file_mode_setting_monthly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_click_single_file_mode_setting_monthly +description: Count of users clicking single file mode setting +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 28d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_28d/20210421145818_i_code_review_click_file_browser_setting_monthly.yml b/config/metrics/counts_28d/20210421145818_i_code_review_click_file_browser_setting_monthly.yml new file mode 100644 index 00000000000..3b7c5be9b84 --- /dev/null +++ b/config/metrics/counts_28d/20210421145818_i_code_review_click_file_browser_setting_monthly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_click_file_browser_setting_monthly +description: Count of users clicking merge request file browser setting +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 28d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_28d/20210421145945_i_code_review_click_whitespace_setting_monthly.yml b/config/metrics/counts_28d/20210421145945_i_code_review_click_whitespace_setting_monthly.yml new file mode 100644 index 00000000000..31fe497fe0e --- /dev/null +++ b/config/metrics/counts_28d/20210421145945_i_code_review_click_whitespace_setting_monthly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_click_whitespace_setting_monthly +description: Count of users clicking merge request whitespae setting +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 28d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_28d/20210422101516_i_code_review_diff_view_inline_monthly.yml b/config/metrics/counts_28d/20210422101516_i_code_review_diff_view_inline_monthly.yml new file mode 100644 index 00000000000..821f7244209 --- /dev/null +++ b/config/metrics/counts_28d/20210422101516_i_code_review_diff_view_inline_monthly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_view_inline_monthly +description: Count of users with merge request view type as inline +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 28d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_28d/20210422101613_i_code_review_diff_view_parallel_monthly.yml b/config/metrics/counts_28d/20210422101613_i_code_review_diff_view_parallel_monthly.yml new file mode 100644 index 00000000000..04ff06143af --- /dev/null +++ b/config/metrics/counts_28d/20210422101613_i_code_review_diff_view_parallel_monthly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_view_parallel_monthly +description: Count of users with merge request view type as parallel +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 28d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_28d/20210422101753_i_code_review_file_browser_tree_view_monthly.yml b/config/metrics/counts_28d/20210422101753_i_code_review_file_browser_tree_view_monthly.yml new file mode 100644 index 00000000000..ad5b5e835c1 --- /dev/null +++ b/config/metrics/counts_28d/20210422101753_i_code_review_file_browser_tree_view_monthly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_file_browser_tree_view_monthly +description: Count of users with merge request file tree setting +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 28d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_28d/20210422101852_i_code_review_file_browser_list_view_monthly.yml b/config/metrics/counts_28d/20210422101852_i_code_review_file_browser_list_view_monthly.yml new file mode 100644 index 00000000000..c71bacd6004 --- /dev/null +++ b/config/metrics/counts_28d/20210422101852_i_code_review_file_browser_list_view_monthly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_file_browser_list_view_monthly +description: Count of users with merge request file list setting +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 28d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_28d/20210422101928_i_code_review_diff_show_whitespace_monthly.yml b/config/metrics/counts_28d/20210422101928_i_code_review_diff_show_whitespace_monthly.yml new file mode 100644 index 00000000000..0d17fa0d965 --- /dev/null +++ b/config/metrics/counts_28d/20210422101928_i_code_review_diff_show_whitespace_monthly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_show_whitespace_monthly +description: Count of users with show whitespace enabled +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 28d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_28d/20210422102010_i_code_review_diff_hide_whitespace_monthly.yml b/config/metrics/counts_28d/20210422102010_i_code_review_diff_hide_whitespace_monthly.yml new file mode 100644 index 00000000000..6038bb5cda8 --- /dev/null +++ b/config/metrics/counts_28d/20210422102010_i_code_review_diff_hide_whitespace_monthly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_hide_whitespace_monthly +description: Count of users with show whitespace disabled +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 28d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_28d/20210422102121_i_code_review_diff_single_file_monthly.yml b/config/metrics/counts_28d/20210422102121_i_code_review_diff_single_file_monthly.yml new file mode 100644 index 00000000000..3cfcdf23a47 --- /dev/null +++ b/config/metrics/counts_28d/20210422102121_i_code_review_diff_single_file_monthly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_single_file_monthly +description: Count of users with single file mode enabled +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 28d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_28d/20210422102202_i_code_review_diff_multiple_files_monthly.yml b/config/metrics/counts_28d/20210422102202_i_code_review_diff_multiple_files_monthly.yml new file mode 100644 index 00000000000..16784a0c408 --- /dev/null +++ b/config/metrics/counts_28d/20210422102202_i_code_review_diff_multiple_files_monthly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_multiple_files_monthly +description: Count of users with single mode disabled +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 28d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_7d/20210421144349_i_code_review_click_single_file_mode_setting_weekly.yml b/config/metrics/counts_7d/20210421144349_i_code_review_click_single_file_mode_setting_weekly.yml new file mode 100644 index 00000000000..179d2fa6e47 --- /dev/null +++ b/config/metrics/counts_7d/20210421144349_i_code_review_click_single_file_mode_setting_weekly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_click_single_file_mode_setting_weekly +description: Count of users clicking single file mode setting +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 7d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_7d/20210421145814_i_code_review_click_file_browser_setting_weekly.yml b/config/metrics/counts_7d/20210421145814_i_code_review_click_file_browser_setting_weekly.yml new file mode 100644 index 00000000000..50264bf27cf --- /dev/null +++ b/config/metrics/counts_7d/20210421145814_i_code_review_click_file_browser_setting_weekly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_click_file_browser_setting_weekly +description: Count of users with merge request file list setting +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 7d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_7d/20210421145942_i_code_review_click_whitespace_setting_weekly.yml b/config/metrics/counts_7d/20210421145942_i_code_review_click_whitespace_setting_weekly.yml new file mode 100644 index 00000000000..3206bac8b49 --- /dev/null +++ b/config/metrics/counts_7d/20210421145942_i_code_review_click_whitespace_setting_weekly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_click_whitespace_setting_weekly +description: Count of users clicking merge request whitespae setting +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 7d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_7d/20210422101512_i_code_review_diff_view_inline_weekly.yml b/config/metrics/counts_7d/20210422101512_i_code_review_diff_view_inline_weekly.yml new file mode 100644 index 00000000000..7a3a606c886 --- /dev/null +++ b/config/metrics/counts_7d/20210422101512_i_code_review_diff_view_inline_weekly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_view_inline_weekly +description: Count of users with merge request view type as inline +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 7d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_7d/20210422101609_i_code_review_diff_view_parallel_weekly.yml b/config/metrics/counts_7d/20210422101609_i_code_review_diff_view_parallel_weekly.yml new file mode 100644 index 00000000000..36dbeae82e7 --- /dev/null +++ b/config/metrics/counts_7d/20210422101609_i_code_review_diff_view_parallel_weekly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_view_parallel_weekly +description: Count of users with merge request view type as parallel +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 7d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_7d/20210422101750_i_code_review_file_browser_tree_view_weekly.yml b/config/metrics/counts_7d/20210422101750_i_code_review_file_browser_tree_view_weekly.yml new file mode 100644 index 00000000000..ef74ff6613f --- /dev/null +++ b/config/metrics/counts_7d/20210422101750_i_code_review_file_browser_tree_view_weekly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_file_browser_tree_view_weekly +description: Count of users with merge request file tree setting +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 7d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_7d/20210422101849_i_code_review_file_browser_list_view_weekly.yml b/config/metrics/counts_7d/20210422101849_i_code_review_file_browser_list_view_weekly.yml new file mode 100644 index 00000000000..e2b9f141bb7 --- /dev/null +++ b/config/metrics/counts_7d/20210422101849_i_code_review_file_browser_list_view_weekly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_file_browser_list_view_weekly +description: Count of users with merge request file list setting +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 7d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_7d/20210422101925_i_code_review_diff_show_whitespace_weekly.yml b/config/metrics/counts_7d/20210422101925_i_code_review_diff_show_whitespace_weekly.yml new file mode 100644 index 00000000000..6cca3840faa --- /dev/null +++ b/config/metrics/counts_7d/20210422101925_i_code_review_diff_show_whitespace_weekly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_show_whitespace_weekly +description: Count of users with show whitespace enabled +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 7d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_7d/20210422102007_i_code_review_diff_hide_whitespace_weekly.yml b/config/metrics/counts_7d/20210422102007_i_code_review_diff_hide_whitespace_weekly.yml new file mode 100644 index 00000000000..c1fccc9f774 --- /dev/null +++ b/config/metrics/counts_7d/20210422102007_i_code_review_diff_hide_whitespace_weekly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_hide_whitespace_weekly +description: Count of users with show whitespace disabled +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 7d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_7d/20210422102118_i_code_review_diff_single_file_weekly.yml b/config/metrics/counts_7d/20210422102118_i_code_review_diff_single_file_weekly.yml new file mode 100644 index 00000000000..3b1abf6e0e1 --- /dev/null +++ b/config/metrics/counts_7d/20210422102118_i_code_review_diff_single_file_weekly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_single_file_weekly +description: Count of users with single file mode enabled +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 7d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/config/metrics/counts_7d/20210422102159_i_code_review_diff_multiple_files_weekly.yml b/config/metrics/counts_7d/20210422102159_i_code_review_diff_multiple_files_weekly.yml new file mode 100644 index 00000000000..b3105b041f8 --- /dev/null +++ b/config/metrics/counts_7d/20210422102159_i_code_review_diff_multiple_files_weekly.yml @@ -0,0 +1,19 @@ +--- +key_path: redis_hll_counters.code_review.i_code_review_diff_multiple_files_weekly +description: Count of users with single mode disabled +product_section: dev +product_stage: create +product_group: group::code review +product_category: code_review +value_type: number +status: implemented +milestone: '13.12' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/59979 +time_frame: 7d +data_source: redis_hll +distribution: + - ce +tier: + - free + - premium + - ultimate diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 57a3b55f88f..1744396d995 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -13558,9 +13558,9 @@ Group member relation. | Value | Description | | ----- | ----------- | -| <a id="groupmemberrelationdescendants"></a>`DESCENDANTS` | Descendants members. | -| <a id="groupmemberrelationdirect"></a>`DIRECT` | Direct members. | -| <a id="groupmemberrelationinherited"></a>`INHERITED` | Inherited members. | +| <a id="groupmemberrelationdescendants"></a>`DESCENDANTS` | Members in the group's subgroups. | +| <a id="groupmemberrelationdirect"></a>`DIRECT` | Members in the group itself. | +| <a id="groupmemberrelationinherited"></a>`INHERITED` | Members in the group's ancestor groups. | ### `HealthStatus` diff --git a/doc/development/i18n/merging_translations.md b/doc/development/i18n/merging_translations.md index 553820733e7..48474a68d16 100644 --- a/doc/development/i18n/merging_translations.md +++ b/doc/development/i18n/merging_translations.md @@ -78,3 +78,10 @@ recreate it with the following steps: 1. Select the `gitlab-org/gitlab` repository. 1. In `Select Branches for Translation`, select `master`. 1. Ensure the `Service Branch Name` is `master-i18n`. + +## Manually update the translation levels + +There's no automated way to pull the translation levels from CrowdIn, to display +this information in the language selection dropdown. Therefore, the translation +levels are hard-coded in the `TRANSLATION_LEVELS` constant in [`i18n.rb`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/i18n.rb), +and must be regularly updated. diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md index a5ed7ad2245..41b5d3179ed 100644 --- a/doc/development/testing_guide/best_practices.md +++ b/doc/development/testing_guide/best_practices.md @@ -168,7 +168,7 @@ can be used: ```ruby RSpec.describe API::Search, factory_default: :keep do - let_it_be(:namespace) { create_default(:namespace).freeze } + let_it_be(:namespace) { create_default(:namespace) } ``` Then every project we create uses this `namespace`, without us having to pass @@ -186,7 +186,7 @@ projects we create are ones we ask for (76/208). There is benefit in setting a default value for projects as well: ```ruby - let_it_be(:project) { create_default(:project).freeze } + let_it_be(:project) { create_default(:project) } ``` In this case, the `total time` and `top-level time` numbers match more closely: diff --git a/doc/development/testing_guide/flaky_tests.md b/doc/development/testing_guide/flaky_tests.md index a9af8f03d63..6b1c7a7eb58 100644 --- a/doc/development/testing_guide/flaky_tests.md +++ b/doc/development/testing_guide/flaky_tests.md @@ -76,6 +76,33 @@ For instance `RETRIES=1 bin/rspec ...` would retry the failing examples once. - [Replace FFaker factory data with sequences](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/29643): <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10184> - [Transient failure in spec/finders/issues_finder_spec.rb](https://gitlab.com/gitlab-org/gitlab-foss/-/issues/30211#note_26707685): <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10404> +### Order-dependent flaky tests + +These flaky tests can fail depending on the order they run with other tests. For example: + +- <https://gitlab.com/gitlab-org/gitlab/-/issues/327668> + +To identify the tests that lead to such failure, we can use `rspec --bisect`, +which would give us the minimal test combination to reproduce the failure: + +```shell +rspec --bisect ee/spec/services/ee/merge_requests/update_service_spec.rb ee/spec/services/ee/notes/quick_actions_service_spec.rb ee/spec/services/epic_links/create_service_spec.rb ee/spec/services/ee/issuable/bulk_update_service_spec.rb +Bisect started using options: "ee/spec/services/ee/merge_requests/update_service_spec.rb ee/spec/services/ee/notes/quick_actions_service_spec.rb ee/spec/services/epic_links/create_service_spec.rb ee/spec/services/ee/issuable/bulk_update_service_spec.rb" +Running suite to find failures... (2 minutes 18.4 seconds) +Starting bisect with 3 failing examples and 144 non-failing examples. +Checking that failure(s) are order-dependent... failure appears to be order-dependent + +Round 1: bisecting over non-failing examples 1-144 . ignoring examples 1-72 (1 minute 11.33 seconds) +... +Round 7: bisecting over non-failing examples 132-133 . ignoring example 132 (43.78 seconds) +Bisect complete! Reduced necessary non-failing examples from 144 to 1 in 8 minutes 31 seconds. + +The minimal reproduction command is: + rspec ./ee/spec/services/ee/issuable/bulk_update_service_spec.rb[1:2:1:1:1:1,1:2:1:2:1:1,1:2:1:3:1] ./ee/spec/services/epic_links/create_service_spec.rb[1:1:2:2:6:4] +``` + +We can reproduce the test failure with the reproduction command above. If we change the order of the tests, the test would pass. + ### Time-sensitive flaky tests - <https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/10046> diff --git a/doc/development/usage_ping/dictionary.md b/doc/development/usage_ping/dictionary.md index 9a256b85983..036683b3d18 100644 --- a/doc/development/usage_ping/dictionary.md +++ b/doc/development/usage_ping/dictionary.md @@ -8048,6 +8048,222 @@ Status: `data_available` Tiers: `free`, `premium`, `ultimate` +### `redis_hll_counters.code_review.i_code_review_click_file_browser_setting_monthly` + +Count of users clicking merge request file browser setting + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210421145818_i_code_review_click_file_browser_setting_monthly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_click_file_browser_setting_weekly` + +Count of users with merge request file list setting + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210421145814_i_code_review_click_file_browser_setting_weekly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_click_single_file_mode_setting_monthly` + +Count of users clicking single file mode setting + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210421144352_i_code_review_click_single_file_mode_setting_monthly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_click_single_file_mode_setting_weekly` + +Count of users clicking single file mode setting + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210421144349_i_code_review_click_single_file_mode_setting_weekly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_click_whitespace_setting_monthly` + +Count of users clicking merge request whitespae setting + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210421145945_i_code_review_click_whitespace_setting_monthly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_click_whitespace_setting_weekly` + +Count of users clicking merge request whitespae setting + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210421145942_i_code_review_click_whitespace_setting_weekly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_hide_whitespace_monthly` + +Count of users with show whitespace disabled + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422102010_i_code_review_diff_hide_whitespace_monthly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_hide_whitespace_weekly` + +Count of users with show whitespace disabled + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422102007_i_code_review_diff_hide_whitespace_weekly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_multiple_files_monthly` + +Count of users with single mode disabled + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422102202_i_code_review_diff_multiple_files_monthly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_multiple_files_weekly` + +Count of users with single mode disabled + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422102159_i_code_review_diff_multiple_files_weekly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_show_whitespace_monthly` + +Count of users with show whitespace enabled + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422101928_i_code_review_diff_show_whitespace_monthly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_show_whitespace_weekly` + +Count of users with show whitespace enabled + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422101925_i_code_review_diff_show_whitespace_weekly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_single_file_monthly` + +Count of users with single file mode enabled + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422102121_i_code_review_diff_single_file_monthly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_single_file_weekly` + +Count of users with single file mode enabled + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422102118_i_code_review_diff_single_file_weekly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_view_inline_monthly` + +Count of users with merge request view type as inline + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422101516_i_code_review_diff_view_inline_monthly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_view_inline_weekly` + +Count of users with merge request view type as inline + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422101512_i_code_review_diff_view_inline_weekly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_view_parallel_monthly` + +Count of users with merge request view type as parallel + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422101613_i_code_review_diff_view_parallel_monthly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_diff_view_parallel_weekly` + +Count of users with merge request view type as parallel + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422101609_i_code_review_diff_view_parallel_weekly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + ### `redis_hll_counters.code_review.i_code_review_edit_mr_desc_monthly` Count of unique users per month who edit the description of a merge request @@ -8096,6 +8312,54 @@ Status: `data_available` Tiers: `free`, `premium`, `ultimate` +### `redis_hll_counters.code_review.i_code_review_file_browser_list_view_monthly` + +Count of users with merge request file list setting + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422101852_i_code_review_file_browser_list_view_monthly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_file_browser_list_view_weekly` + +Count of users with merge request file list setting + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422101849_i_code_review_file_browser_list_view_weekly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_file_browser_tree_view_monthly` + +Count of users with merge request file tree setting + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_28d/20210422101753_i_code_review_file_browser_tree_view_monthly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + +### `redis_hll_counters.code_review.i_code_review_file_browser_tree_view_weekly` + +Count of users with merge request file tree setting + +[YAML definition](https://gitlab.com/gitlab-org/gitlab/-/blob/master/config/metrics/counts_7d/20210422101750_i_code_review_file_browser_tree_view_weekly.yml) + +Group: `group::code review` + +Status: `implemented` + +Tiers: `free`, `premium`, `ultimate` + ### `redis_hll_counters.code_review.i_code_review_mr_diffs_monthly` Count of unique merge requests per month with diffs viewed diff --git a/doc/user/clusters/agent/repository.md b/doc/user/clusters/agent/repository.md index 9caa4a89daf..49e5e8c58df 100644 --- a/doc/user/clusters/agent/repository.md +++ b/doc/user/clusters/agent/repository.md @@ -94,3 +94,41 @@ gitops: # If 'paths' is not specified or is an empty list, the configuration below is used - glob: '/**/*.{yaml,yml,json}' ``` + +## Surface network security alerts from cluster to GitLab + +The GitLab Agent provides an [integration with Cilium](index.md#kubernetes-network-security-alerts). +To integrate, add a top-level `cilium` section to your `config.yml` file. Currently, the +only configuration option is the Hubble relay address: + +```yaml +cilium: + hubble_relay_address: "<hubble-relay-host>:<hubble-relay-port>" +``` + +If your Cilium integration was performed through GitLab Managed Apps, you can use `hubble-relay.gitlab-managed-apps.svc.cluster.local:80` as the address: + +```yaml +cilium: + hubble_relay_address: "hubble-relay.gitlab-managed-apps.svc.cluster.local:80" +``` + +## Debugging + +To debug the cluster-side component (`agentk`) of the GitLab Kubernetes Agent, set the log +level according to the available options: + +- `off` +- `warning` +- `error` +- `info` +- `debug` + +The log level defaults to `info`. You can change it by using a top-level `observability` +section in the configuration file, for example: + +```yaml +observability: + logging: + level: debug +``` diff --git a/lib/api/issues.rb b/lib/api/issues.rb index 4f2ac73c0d3..c844655f0b3 100644 --- a/lib/api/issues.rb +++ b/lib/api/issues.rb @@ -249,7 +249,6 @@ module API authorize! :create_issue, user_project issue_params = declared_params(include_missing: false) - issue_params[:system_note_timestamp] = params[:created_at] issue_params = convert_parameters_from_legacy_format(issue_params) @@ -293,8 +292,6 @@ module API issue = user_project.issues.find_by!(iid: params.delete(:issue_iid)) authorize! :update_issue, issue - issue.system_note_timestamp = params[:updated_at] - update_params = declared_params(include_missing: false).merge(request: request, api: true) update_params = convert_parameters_from_legacy_format(update_params) diff --git a/lib/gitlab/auth/scope_validator.rb b/lib/gitlab/auth/scope_validator.rb new file mode 100644 index 00000000000..de4c36ad594 --- /dev/null +++ b/lib/gitlab/auth/scope_validator.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +# Wrapper around a RequestAuthenticator to +# perform authorization of scopes. Access is limited to +# only those methods needed to validate that an API user +# has at least one permitted scope. +module Gitlab + module Auth + class ScopeValidator + def initialize(api_user, request_authenticator) + @api_user = api_user + @request_authenticator = request_authenticator + end + + def valid_for?(permitted) + return true unless @api_user + return true if permitted.none? + + scopes = permitted.map { |s| API::Scope.new(s) } + @request_authenticator.valid_access_token?(scopes: scopes) + end + end + end +end diff --git a/lib/gitlab/graphql/authorize/object_authorization.rb b/lib/gitlab/graphql/authorize/object_authorization.rb index 0bc87108871..f13acc9ea27 100644 --- a/lib/gitlab/graphql/authorize/object_authorization.rb +++ b/lib/gitlab/graphql/authorize/object_authorization.rb @@ -4,10 +4,11 @@ module Gitlab module Graphql module Authorize class ObjectAuthorization - attr_reader :abilities + attr_reader :abilities, :permitted_scopes - def initialize(abilities) + def initialize(abilities, scopes = %i[api read_api]) @abilities = Array.wrap(abilities).flatten + @permitted_scopes = Array.wrap(scopes) end def none? @@ -18,7 +19,13 @@ module Gitlab abilities.present? end - def ok?(object, current_user) + def ok?(object, current_user, scope_validator: nil) + scopes_ok?(scope_validator) && abilities_ok?(object, current_user) + end + + private + + def abilities_ok?(object, current_user) return true if none? subject = object.try(:declarative_policy_subject) || object @@ -26,6 +33,12 @@ module Gitlab Ability.allowed?(current_user, ability, subject) end end + + def scopes_ok?(validator) + return true unless validator.present? + + validator.valid_for?(permitted_scopes) + end end end end diff --git a/lib/gitlab/i18n.rb b/lib/gitlab/i18n.rb index 3b19ae3d7ff..12a3878e966 100644 --- a/lib/gitlab/i18n.rb +++ b/lib/gitlab/i18n.rb @@ -4,20 +4,6 @@ module Gitlab module I18n extend self - # Languages with less then 2% of available translations will not - # be available in the UI. - # https://gitlab.com/gitlab-org/gitlab/-/issues/221012 - NOT_AVAILABLE_IN_UI = %w[ - fil_PH - pl_PL - nl_NL - id_ID - cs_CZ - bg - eo - gl_ES - ].freeze - AVAILABLE_LANGUAGES = { 'bg' => 'Bulgarian - български', 'cs_CZ' => 'Czech - čeština', @@ -42,9 +28,49 @@ module Gitlab 'zh_HK' => 'Chinese, Traditional (Hong Kong) - 繁體中文 (香港)', 'zh_TW' => 'Chinese, Traditional (Taiwan) - 繁體中文 (台灣)' }.freeze + private_constant :AVAILABLE_LANGUAGES + + # Languages with less then MINIMUM_TRANSLATION_LEVEL% of available translations will not + # be available in the UI. + # https://gitlab.com/gitlab-org/gitlab/-/issues/221012 + MINIMUM_TRANSLATION_LEVEL = 2 + + # Currently monthly updated manually by ~group::import PM. + # https://gitlab.com/gitlab-org/gitlab/-/issues/18923 + TRANSLATION_LEVELS = { + 'bg' => 1, + 'cs_CZ' => 1, + 'de' => 20, + 'en' => 100, + 'eo' => 1, + 'es' => 44, + 'fil_PH' => 1, + 'fr' => 14, + 'gl_ES' => 1, + 'id_ID' => 0, + 'it' => 3, + 'ja' => 49, + 'ko' => 15, + 'nl_NL' => 1, + 'pl_PL' => 1, + 'pt_BR' => 23, + 'ru' => 34, + 'tr_TR' => 18, + 'uk' => 46, + 'zh_CN' => 78, + 'zh_HK' => 3, + 'zh_TW' => 4 + }.freeze + private_constant :TRANSLATION_LEVELS def selectable_locales - AVAILABLE_LANGUAGES.reject { |key, _value| NOT_AVAILABLE_IN_UI.include? key } + @selectable_locales ||= AVAILABLE_LANGUAGES.reject do |code, _name| + percentage_translated_for(code) < MINIMUM_TRANSLATION_LEVEL + end + end + + def percentage_translated_for(code) + TRANSLATION_LEVELS.fetch(code, 0) end def available_locales diff --git a/lib/gitlab/pagination/gitaly_keyset_pager.rb b/lib/gitlab/pagination/gitaly_keyset_pager.rb index 1350168967e..b05891066ac 100644 --- a/lib/gitlab/pagination/gitaly_keyset_pager.rb +++ b/lib/gitlab/pagination/gitaly_keyset_pager.rb @@ -26,11 +26,11 @@ module Gitlab private def keyset_pagination_enabled? - Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: true) && params[:pagination] == 'keyset' + Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: :yaml) && params[:pagination] == 'keyset' end def paginate_first_page? - Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: true) && (params[:page].blank? || params[:page].to_i == 1) + Feature.enabled?(:branch_list_keyset_pagination, project, default_enabled: :yaml) && (params[:page].blank? || params[:page].to_i == 1) end def paginate_via_gitaly(finder) diff --git a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml index 18c5dc73de2..2f5c1de0b74 100644 --- a/lib/gitlab/usage_data_counters/known_events/code_review_events.yml +++ b/lib/gitlab/usage_data_counters/known_events/code_review_events.yml @@ -204,3 +204,59 @@ category: code_review aggregation: weekly feature_flag: usage_data_i_code_review_user_labels_changed +# Diff settings events +- name: i_code_review_click_single_file_mode_setting + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: diff_settings_usage_data +- name: i_code_review_click_file_browser_setting + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: diff_settings_usage_data +- name: i_code_review_click_whitespace_setting + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: diff_settings_usage_data +- name: i_code_review_diff_view_inline + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: diff_settings_usage_data +- name: i_code_review_diff_view_parallel + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: diff_settings_usage_data +- name: i_code_review_file_browser_tree_view + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: diff_settings_usage_data +- name: i_code_review_file_browser_list_view + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: diff_settings_usage_data +- name: i_code_review_diff_show_whitespace + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: diff_settings_usage_data +- name: i_code_review_diff_hide_whitespace + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: diff_settings_usage_data +- name: i_code_review_diff_single_file + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: diff_settings_usage_data +- name: i_code_review_diff_multiple_files + redis_slot: code_review + category: code_review + aggregation: weekly + feature_flag: diff_settings_usage_data diff --git a/lib/sidebars/projects/menus/merge_requests_menu.rb b/lib/sidebars/projects/menus/merge_requests_menu.rb new file mode 100644 index 00000000000..fe501667d37 --- /dev/null +++ b/lib/sidebars/projects/menus/merge_requests_menu.rb @@ -0,0 +1,70 @@ +# frozen_string_literal: true + +module Sidebars + module Projects + module Menus + class MergeRequestsMenu < ::Sidebars::Menu + override :link + def link + project_merge_requests_path(context.project) + end + + override :extra_container_html_options + def extra_container_html_options + { + class: 'shortcuts-merge_requests' + } + end + + override :title + def title + _('Merge requests') + end + + override :title_html_options + def title_html_options + { + id: 'js-onboarding-mr-link' + } + end + + override :sprite_icon + def sprite_icon + 'git-merge' + end + + override :render? + def render? + can?(context.current_user, :read_merge_request, context.project) && + context.project.repo_exists? + end + + override :has_pill? + def has_pill? + true + end + + override :pill_count + def pill_count + @pill_count ||= context.project.open_merge_requests_count + end + + override :pill_html_options + def pill_html_options + { + class: 'merge_counter js-merge-counter' + } + end + + override :active_routes + def active_routes + if context.project.issues_enabled? + { controller: :merge_requests } + else + { controller: [:merge_requests, :milestones] } + end + end + end + end + end +end diff --git a/lib/sidebars/projects/panel.rb b/lib/sidebars/projects/panel.rb index 0e2daca9194..1db4d55740a 100644 --- a/lib/sidebars/projects/panel.rb +++ b/lib/sidebars/projects/panel.rb @@ -13,6 +13,7 @@ module Sidebars add_menu(Sidebars::Projects::Menus::IssuesMenu.new(context)) add_menu(Sidebars::Projects::Menus::ExternalIssueTrackerMenu.new(context)) add_menu(Sidebars::Projects::Menus::LabelsMenu.new(context)) + add_menu(Sidebars::Projects::Menus::MergeRequestsMenu.new(context)) end override :render_raw_menus_partial diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 5ca4e7dad0c..f350f6ece5d 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -30989,6 +30989,9 @@ msgstr "" msgid "SuperSonics|Your subscription" msgstr "" +msgid "SuperSonics|Your subscription was successfully activated. You can see the details below." +msgstr "" + msgid "Support" msgstr "" @@ -37792,6 +37795,9 @@ msgstr "" msgid "https://your-bitbucket-server" msgstr "" +msgid "i18n|%{language} (%{percent_translated}%% translated)" +msgstr "" + msgid "image diff" msgstr "" diff --git a/qa/qa/page/project/menu.rb b/qa/qa/page/project/menu.rb index 4e8178c3cf6..a7c6637bb89 100644 --- a/qa/qa/page/project/menu.rb +++ b/qa/qa/page/project/menu.rb @@ -14,7 +14,6 @@ module QA include SubMenus::Packages view 'app/views/layouts/nav/sidebar/_project_menus.html.haml' do - element :merge_requests_link element :snippets_link element :members_link end @@ -25,7 +24,7 @@ module QA def click_merge_requests within_sidebar do - click_element(:merge_requests_link) + click_element(:sidebar_menu_link, menu_item: 'Merge requests') end end diff --git a/spec/features/merge_request/batch_comments_spec.rb b/spec/features/merge_request/batch_comments_spec.rb index 19680a827bf..5b11d9cb919 100644 --- a/spec/features/merge_request/batch_comments_spec.rb +++ b/spec/features/merge_request/batch_comments_spec.rb @@ -34,7 +34,7 @@ RSpec.describe 'Merge request > Batch comments', :js do expect(page).to have_css('.review-bar-component') - expect(find('.review-bar-content .btn-success')).to have_content('1') + expect(find('.review-bar-content .btn-confirm')).to have_content('1') end it 'publishes review' do @@ -157,7 +157,7 @@ RSpec.describe 'Merge request > Batch comments', :js do expect(find('.new .draft-note-component')).to have_content('Line is wrong') expect(find('.old .draft-note-component')).to have_content('Another wrong line') - expect(find('.review-bar-content .btn-success')).to have_content('2') + expect(find('.review-bar-content .btn-confirm')).to have_content('2') end end diff --git a/spec/features/profiles/user_edit_preferences_spec.rb b/spec/features/profiles/user_edit_preferences_spec.rb index 3129e4bd952..232402f802e 100644 --- a/spec/features/profiles/user_edit_preferences_spec.rb +++ b/spec/features/profiles/user_edit_preferences_spec.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + require 'spec_helper' RSpec.describe 'User edit preferences profile', :js do @@ -63,17 +64,4 @@ RSpec.describe 'User edit preferences profile', :js do expect(page).to have_content('Failed to save preferences.') end end - - describe 'User language' do - let(:user) { create(:user, preferred_language: :es) } - - it 'shows the user preferred language by default' do - expect(page).to have_select( - 'user[preferred_language]', - selected: 'Spanish - español', - options: Gitlab::I18n.selectable_locales.values, - visible: :all - ) - end - end end diff --git a/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap b/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap index f8c68ca4c83..d9f5ba0bade 100644 --- a/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap +++ b/spec/frontend/design_management/components/design_notes/__snapshots__/design_reply_form_spec.js.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Design reply form component renders button text as "Comment" when creating a comment 1`] = ` -"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn btn-success btn-md disabled gl-button\\"> +"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn gl-mr-3 gl-w-auto! btn-confirm btn-md disabled gl-button\\"> <!----> <!----> <span class=\\"gl-button-text\\"> Comment @@ -9,7 +9,7 @@ exports[`Design reply form component renders button text as "Comment" when creat `; exports[`Design reply form component renders button text as "Save comment" when creating a comment 1`] = ` -"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn btn-success btn-md disabled gl-button\\"> +"<button data-track-event=\\"click_button\\" data-qa-selector=\\"save_comment_button\\" type=\\"submit\\" disabled=\\"disabled\\" class=\\"btn gl-mr-3 gl-w-auto! btn-confirm btn-md disabled gl-button\\"> <!----> <!----> <span class=\\"gl-button-text\\"> Save comment diff --git a/spec/helpers/preferences_helper_spec.rb b/spec/helpers/preferences_helper_spec.rb index 4d7083c4ca7..adb29f6634d 100644 --- a/spec/helpers/preferences_helper_spec.rb +++ b/spec/helpers/preferences_helper_spec.rb @@ -121,6 +121,23 @@ RSpec.describe PreferencesHelper do end end + describe '#language_choices' do + it 'lists all the selectable language options with their translation percent' do + stub_const( + 'Gitlab::I18n::TRANSLATION_LEVELS', + 'en' => 100, + 'es' => 65 + ) + + stub_user(preferred_language: :en) + + expect(helper.language_choices).to eq([ + '<option selected="selected" value="en">English (100% translated)</option>', + '<option value="es">Spanish - español (65% translated)</option>' + ].join("\n")) + end + end + def stub_user(messages = {}) if messages.empty? allow(helper).to receive(:current_user).and_return(nil) diff --git a/spec/lib/gitlab/i18n_spec.rb b/spec/lib/gitlab/i18n_spec.rb index ee10739195a..f34de298fc0 100644 --- a/spec/lib/gitlab/i18n_spec.rb +++ b/spec/lib/gitlab/i18n_spec.rb @@ -3,13 +3,21 @@ require 'spec_helper' RSpec.describe Gitlab::I18n do - let(:user) { create(:user, preferred_language: 'es') } + let(:user) { create(:user, preferred_language: :es) } describe '.selectable_locales' do - it 'does not return languages that should not be available in the UI' do - Gitlab::I18n::NOT_AVAILABLE_IN_UI.each do |language| - expect(described_class.selectable_locales).not_to include(language) - end + it 'does not return languages with low translation levels' do + stub_const( + 'Gitlab::I18n::TRANSLATION_LEVELS', + 'pt_BR' => 0, + 'en' => 100, + 'es' => 65 + ) + + expect(described_class.selectable_locales).to eq({ + 'en' => 'English', + 'es' => 'Spanish - español' + }) end end diff --git a/spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb b/spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb new file mode 100644 index 00000000000..cef303fb068 --- /dev/null +++ b/spec/lib/sidebars/projects/menus/merge_requests_menu_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Sidebars::Projects::Menus::MergeRequestsMenu do + let_it_be(:project) { create(:project, :repository) } + + let(:user) { project.owner } + let(:context) { Sidebars::Projects::Context.new(current_user: user, container: project) } + + subject { described_class.new(context) } + + describe '#render?' do + context 'when repository is not present' do + let(:project) { build(:project) } + + it 'returns false' do + expect(subject.render?).to eq false + end + end + + context 'when repository is present' do + context 'when user can read merge requests' do + it 'returns true' do + expect(subject.render?).to eq true + end + end + + context 'when user cannot read merge requests' do + let(:user) { nil } + + it 'returns false' do + expect(subject.render?).to eq false + end + end + end + end + + describe '#pill_count' do + it 'returns zero when there are no open merge requests' do + expect(subject.pill_count).to eq 0 + end + + it 'memoizes the query' do + subject.pill_count + + control = ActiveRecord::QueryRecorder.new do + subject.pill_count + end + + expect(control.count).to eq 0 + end + + context 'when there are open merge requests' do + it 'returns the number of open merge requests' do + create_list(:merge_request, 2, :unique_branches, source_project: project, author: user, state: :opened) + create(:merge_request, source_project: project, state: :merged) + + expect(subject.pill_count).to eq 2 + end + end + end +end diff --git a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb index 1eed1c8e2ae..8dd8ed361ba 100644 --- a/spec/requests/api/graphql/mutations/notes/create/note_spec.rb +++ b/spec/requests/api/graphql/mutations/notes/create/note_spec.rb @@ -31,6 +31,8 @@ RSpec.describe 'Adding a Note' do project.add_developer(current_user) end + it_behaves_like 'a working GraphQL mutation' + it_behaves_like 'a Note mutation that creates a Note' it_behaves_like 'a Note mutation when there are active record validation errors' diff --git a/spec/requests/api/issues/issues_spec.rb b/spec/requests/api/issues/issues_spec.rb index 0fe68be027c..8f10de59526 100644 --- a/spec/requests/api/issues/issues_spec.rb +++ b/spec/requests/api/issues/issues_spec.rb @@ -943,6 +943,34 @@ RSpec.describe API::Issues do it_behaves_like 'issuable update endpoint' do let(:entity) { issue } end + + describe 'updated_at param' do + let(:fixed_time) { Time.new(2001, 1, 1) } + let(:updated_at) { Time.new(2000, 1, 1) } + + before do + travel_to fixed_time + end + + it 'allows admins to set the timestamp' do + put api("/projects/#{project.id}/issues/#{issue.iid}", admin), params: { labels: 'label1', updated_at: updated_at } + + expect(response).to have_gitlab_http_status(:ok) + expect(Time.parse(json_response['updated_at'])).to be_like_time(updated_at) + expect(ResourceLabelEvent.last.created_at).to be_like_time(updated_at) + end + + it 'does not allow other users to set the timestamp' do + reporter = create(:user) + project.add_developer(reporter) + + put api("/projects/#{project.id}/issues/#{issue.iid}", reporter), params: { labels: 'label1', updated_at: updated_at } + + expect(response).to have_gitlab_http_status(:ok) + expect(Time.parse(json_response['updated_at'])).to be_like_time(fixed_time) + expect(ResourceLabelEvent.last.created_at).to be_like_time(fixed_time) + end + end end describe 'DELETE /projects/:id/issues/:issue_iid' do diff --git a/spec/requests/api/issues/post_projects_issues_spec.rb b/spec/requests/api/issues/post_projects_issues_spec.rb index 7f1db620d4f..9d3bd26a200 100644 --- a/spec/requests/api/issues/post_projects_issues_spec.rb +++ b/spec/requests/api/issues/post_projects_issues_spec.rb @@ -330,15 +330,21 @@ RSpec.describe API::Issues do end context 'setting created_at' do + let(:fixed_time) { Time.new(2001, 1, 1) } let(:creation_time) { 2.weeks.ago } let(:params) { { title: 'new issue', labels: 'label, label2', created_at: creation_time } } + before do + travel_to fixed_time + end + context 'by an admin' do it 'sets the creation time on the new issue' do post api("/projects/#{project.id}/issues", admin), params: params expect(response).to have_gitlab_http_status(:created) expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + expect(ResourceLabelEvent.last.created_at).to be_like_time(creation_time) end end @@ -348,6 +354,7 @@ RSpec.describe API::Issues do expect(response).to have_gitlab_http_status(:created) expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + expect(ResourceLabelEvent.last.created_at).to be_like_time(creation_time) end end @@ -356,19 +363,24 @@ RSpec.describe API::Issues do group = create(:group) group_project = create(:project, :public, namespace: group) group.add_owner(user2) + post api("/projects/#{group_project.id}/issues", user2), params: params expect(response).to have_gitlab_http_status(:created) expect(Time.parse(json_response['created_at'])).to be_like_time(creation_time) + expect(ResourceLabelEvent.last.created_at).to be_like_time(creation_time) end end context 'by another user' do it 'ignores the given creation time' do + project.add_developer(user2) + post api("/projects/#{project.id}/issues", user2), params: params expect(response).to have_gitlab_http_status(:created) - expect(Time.parse(json_response['created_at'])).not_to be_like_time(creation_time) + expect(Time.parse(json_response['created_at'])).to be_like_time(fixed_time) + expect(ResourceLabelEvent.last.created_at).to be_like_time(fixed_time) end end end diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb index 8be26784a3d..5b5658da97e 100644 --- a/spec/requests/jwt_controller_spec.rb +++ b/spec/requests/jwt_controller_spec.rb @@ -263,25 +263,21 @@ RSpec.describe JwtController do let(:credential_user) { group_deploy_token.username } let(:credential_password) { group_deploy_token.token } - it_behaves_like 'with valid credentials' + it_behaves_like 'returning response status', :forbidden end context 'with project deploy token' do let(:credential_user) { project_deploy_token.username } let(:credential_password) { project_deploy_token.token } - it_behaves_like 'with valid credentials' + it_behaves_like 'returning response status', :forbidden end context 'with invalid credentials' do let(:credential_user) { 'foo' } let(:credential_password) { 'bar' } - it 'returns unauthorized' do - subject - - expect(response).to have_gitlab_http_status(:unauthorized) - end + it_behaves_like 'returning response status', :unauthorized end end diff --git a/spec/services/auth/dependency_proxy_authentication_service_spec.rb b/spec/services/auth/dependency_proxy_authentication_service_spec.rb index ba50149f53a..1fd1677c7da 100644 --- a/spec/services/auth/dependency_proxy_authentication_service_spec.rb +++ b/spec/services/auth/dependency_proxy_authentication_service_spec.rb @@ -13,28 +13,31 @@ RSpec.describe Auth::DependencyProxyAuthenticationService do describe '#execute' do subject { service.execute(authentication_abilities: nil) } + shared_examples 'returning' do |status:, message:| + it "returns #{message}", :aggregate_failures do + expect(subject[:http_status]).to eq(status) + expect(subject[:message]).to eq(message) + end + end + context 'dependency proxy is not enabled' do before do stub_config(dependency_proxy: { enabled: false }) end - it 'returns not found' do - result = subject - - expect(result[:http_status]).to eq(404) - expect(result[:message]).to eq('dependency proxy not enabled') - end + it_behaves_like 'returning', status: 404, message: 'dependency proxy not enabled' end context 'without a user' do let(:user) { nil } - it 'returns forbidden' do - result = subject + it_behaves_like 'returning', status: 403, message: 'access forbidden' + end + + context 'with a deploy token as user' do + let_it_be(:user) { create(:deploy_token) } - expect(result[:http_status]).to eq(403) - expect(result[:message]).to eq('access forbidden') - end + it_behaves_like 'returning', status: 403, message: 'access forbidden' end context 'with a user' do diff --git a/spec/services/projects/download_service_spec.rb b/spec/services/projects/download_service_spec.rb index 0f743eaa7f5..7d4fce814f5 100644 --- a/spec/services/projects/download_service_spec.rb +++ b/spec/services/projects/download_service_spec.rb @@ -20,8 +20,9 @@ RSpec.describe Projects::DownloadService do context 'for URLs that are on the whitelist' do before do - stub_request(:get, 'http://mycompany.fogbugz.com/rails_sample.jpg').to_return(body: File.read(Rails.root + 'spec/fixtures/rails_sample.jpg')) - stub_request(:get, 'http://mycompany.fogbugz.com/doc_sample.txt').to_return(body: File.read(Rails.root + 'spec/fixtures/doc_sample.txt')) + # `ssrf_filter` resolves the hostname. See https://github.com/carrierwaveuploader/carrierwave/commit/91714adda998bc9e8decf5b1f5d260d808761304 + stub_request(:get, %r{http://[\d\.]+/rails_sample.jpg}).to_return(body: File.read(Rails.root + 'spec/fixtures/rails_sample.jpg')) + stub_request(:get, %r{http://[\d\.]+/doc_sample.txt}).to_return(body: File.read(Rails.root + 'spec/fixtures/doc_sample.txt')) end context 'an image file' do diff --git a/spec/support/helpers/graphql_helpers.rb b/spec/support/helpers/graphql_helpers.rb index ebf22987d50..5dc6945ec5e 100644 --- a/spec/support/helpers/graphql_helpers.rb +++ b/spec/support/helpers/graphql_helpers.rb @@ -396,17 +396,21 @@ module GraphqlHelpers post api('/', current_user, version: 'graphql'), params: { _json: queries }, headers: headers end - def post_graphql(query, current_user: nil, variables: nil, headers: {}) + def post_graphql(query, current_user: nil, variables: nil, headers: {}, token: {}) params = { query: query, variables: serialize_variables(variables) } - post api('/', current_user, version: 'graphql'), params: params, headers: headers + post api('/', current_user, version: 'graphql', **token), params: params, headers: headers - if graphql_errors # Errors are acceptable, but not this one: - expect(graphql_errors).not_to include(a_hash_including('message' => 'Internal server error')) - end + return unless graphql_errors + + # Errors are acceptable, but not this one: + expect(graphql_errors).not_to include(a_hash_including('message' => 'Internal server error')) end - def post_graphql_mutation(mutation, current_user: nil) - post_graphql(mutation.query, current_user: current_user, variables: mutation.variables) + def post_graphql_mutation(mutation, current_user: nil, token: {}) + post_graphql(mutation.query, + current_user: current_user, + variables: mutation.variables, + token: token) end def post_graphql_mutation_with_uploads(mutation, current_user: nil) diff --git a/spec/support/shared_examples/requests/graphql_shared_examples.rb b/spec/support/shared_examples/requests/graphql_shared_examples.rb index a66bc7112fe..d133c5ea641 100644 --- a/spec/support/shared_examples/requests/graphql_shared_examples.rb +++ b/spec/support/shared_examples/requests/graphql_shared_examples.rb @@ -10,6 +10,52 @@ RSpec.shared_examples 'a working graphql query' do end end +RSpec.shared_examples 'a working GraphQL mutation' do + include GraphqlHelpers + + before do + post_graphql_mutation(mutation, current_user: current_user, token: token) + end + + shared_examples 'allows access to the mutation' do + let(:scopes) { ['api'] } + + it_behaves_like 'a working graphql query' do + it 'returns data' do + expect(graphql_data.compact).not_to be_empty + end + end + end + + shared_examples 'prevents access to the mutation' do + let(:scopes) { ['read_api'] } + + it 'does not resolve the mutation' do + expect(graphql_data.compact).to be_empty + expect(graphql_errors).to be_present + end + end + + context 'with a personal access token' do + let(:token) do + pat = create(:personal_access_token, user: current_user, scopes: scopes) + { personal_access_token: pat } + end + + it_behaves_like 'prevents access to the mutation' + it_behaves_like 'allows access to the mutation' + end + + context 'with an OAuth token' do + let(:token) do + { oauth_access_token: create(:oauth_access_token, resource_owner: current_user, scopes: scopes.join(' ')) } + end + + it_behaves_like 'prevents access to the mutation' + it_behaves_like 'allows access to the mutation' + end +end + RSpec.shared_examples 'a mutation on an unauthorized resource' do it_behaves_like 'a mutation that returns top-level errors', errors: [::Gitlab::Graphql::Authorize::AuthorizeResource::RESOURCE_ACCESS_ERROR] diff --git a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb index 39cc4a3fdf1..c501c418466 100644 --- a/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb +++ b/spec/views/layouts/nav/sidebar/_project.html.haml_spec.rb @@ -243,6 +243,20 @@ RSpec.describe 'layouts/nav/sidebar/_project' do end end + describe 'Merge Requests' do + it 'has a link to the merge request list path' do + render + + expect(rendered).to have_link('Merge requests', href: project_merge_requests_path(project), class: 'shortcuts-merge_requests') + end + + it 'shows pill with the number of merge requests' do + render + + expect(rendered).to have_css('span.badge.badge-pill.merge_counter.js-merge-counter') + end + end + describe 'packages tab' do before do stub_container_registry_config(enabled: true) |