diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-17 21:09:01 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-03-17 21:09:01 +0300 |
commit | e388691e4a5b5b69be903c7eceb606b853719cd5 (patch) | |
tree | 97875b98a9a9c7b0dfe9245ce70d5e38ac3a3549 | |
parent | cb840235d7fb4001dab266c614bd2cf59036fe18 (diff) |
Add latest changes from gitlab-org/gitlab@master
75 files changed, 640 insertions, 210 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 742a3a2d972..faaf5c97a86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ documentation](doc/development/changelog.md) for instructions on adding your own entry. +## 13.9.4 (2021-03-17) + +### Security (1 change) + +- Patch Kramdown syntax highlighter gem. + + ## 13.9.3 (2021-03-08) ### Fixed (4 changes) @@ -610,6 +617,13 @@ entry. - Apply new GitLab UI for buttons in pipeline schedules. +## 13.8.6 (2021-03-17) + +### Security (1 change) + +- Patch Kramdown syntax highlighter gem. + + ## 13.8.5 (2021-03-04) ### Security (6 changes) @@ -1022,6 +1036,13 @@ entry. - Add verbiage + link sast to show it's in core. !51935 +## 13.7.9 (2021-03-17) + +### Security (1 change) + +- Patch Kramdown syntax highlighter gem. + + ## 13.7.8 (2021-03-04) ### Security (5 changes) diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js index cb22ae3e02c..f0c39d9cf74 100644 --- a/app/assets/javascripts/boards/index.js +++ b/app/assets/javascripts/boards/index.js @@ -72,14 +72,6 @@ export default () => { boardsStore.setTimeTrackingLimitToHours($boardApp.dataset.timeTrackingLimitToHours); } - if (gon?.features?.boardsFilteredSearch) { - import('~/boards/filtered_search') - .then(({ default: initFilteredSearch }) => { - initFilteredSearch(apolloProvider); - }) - .catch(() => {}); - } - // eslint-disable-next-line @gitlab/no-runtime-template-compiler issueBoardsApp = new Vue({ el: $boardApp, diff --git a/app/assets/javascripts/environments/components/enable_review_app_modal.vue b/app/assets/javascripts/environments/components/enable_review_app_modal.vue index 2494968857c..b0c0f83b88a 100644 --- a/app/assets/javascripts/environments/components/enable_review_app_modal.vue +++ b/app/assets/javascripts/environments/components/enable_review_app_modal.vue @@ -10,6 +10,7 @@ export default { GlSprintf, ModalCopyButton, }, + inject: ['defaultBranchName'], props: { modalId: { type: String, @@ -28,7 +29,11 @@ export default { modalInfo: { closeText: s__('EnableReviewApp|Close'), copyToClipboardText: s__('EnableReviewApp|Copy snippet text'), - copyString: `deploy_review: + title: s__('ReviewApp|Enable Review App'), + }, + computed: { + modalInfoCopyStr() { + return `deploy_review: stage: deploy script: - echo "Deploy a review app" @@ -38,8 +43,8 @@ export default { only: - branches except: - - master`, - title: s__('ReviewApp|Enable Review App'), + - ${this.defaultBranchName}`; + }, }, }; </script> @@ -75,7 +80,9 @@ export default { </gl-sprintf> </p> <div class="gl-display-flex align-items-start"> - <pre class="gl-w-full"> {{ $options.modalInfo.copyString }} </pre> + <pre class="gl-w-full" data-testid="enable-review-app-copy-string"> + {{ modalInfoCopyStr }} </pre + > <modal-copy-button :title="$options.modalInfo.copyToClipboardText" :text="$options.modalInfo.copyString" @@ -90,7 +97,9 @@ export default { <strong>{{ content }}</strong> </template> <template #link="{ content }"> - <gl-link href="blob/master/.gitlab-ci.yml" target="_blank">{{ content }}</gl-link> + <gl-link :href="`blob/${defaultBranchName}/.gitlab-ci.yml`" target="_blank">{{ + content + }}</gl-link> </template> </gl-sprintf> </p> diff --git a/app/assets/javascripts/environments/index.js b/app/assets/javascripts/environments/index.js index 68348648e61..b99872f7a6c 100644 --- a/app/assets/javascripts/environments/index.js +++ b/app/assets/javascripts/environments/index.js @@ -22,6 +22,7 @@ export default () => { apolloProvider, provide: { projectPath: el.dataset.projectPath, + defaultBranchName: el.dataset.defaultBranchName, }, data() { const environmentsData = el.dataset; diff --git a/app/assets/javascripts/feature_flags/components/form.vue b/app/assets/javascripts/feature_flags/components/form.vue index f6a14d9996f..1f59b709aa2 100644 --- a/app/assets/javascripts/feature_flags/components/form.vue +++ b/app/assets/javascripts/feature_flags/components/form.vue @@ -314,7 +314,7 @@ export default { <h4>{{ s__('FeatureFlags|Strategies') }}</h4> <div class="flex align-items-baseline justify-content-between"> <p class="mr-3">{{ $options.translations.newHelpText }}</p> - <gl-button variant="success" category="secondary" @click="addStrategy"> + <gl-button variant="confirm" category="secondary" @click="addStrategy"> {{ s__('FeatureFlags|Add strategy') }} </gl-button> </div> @@ -575,7 +575,7 @@ export default { ref="submitButton" :disabled="readOnly" type="button" - variant="success" + variant="confirm" class="js-ff-submit col-xs-12" @click="handleSubmit" >{{ submitText }}</gl-button diff --git a/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue b/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue index 627af202028..1765a2f3d5d 100644 --- a/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue +++ b/app/assets/javascripts/monitoring/components/duplicate_dashboard_form.vue @@ -23,7 +23,7 @@ export default { }, }, radioVals: { - /* Use the default branch (e.g. master) */ + /* Use the default branch (e.g. main) */ DEFAULT: 'DEFAULT', /* Create a new branch */ NEW: 'NEW', diff --git a/app/assets/javascripts/mr_notes/init_notes.js b/app/assets/javascripts/mr_notes/init_notes.js index 9a93e90c2bb..ff194d1a171 100644 --- a/app/assets/javascripts/mr_notes/init_notes.js +++ b/app/assets/javascripts/mr_notes/init_notes.js @@ -25,6 +25,9 @@ export default () => { return { noteableData, + endpoints: { + metadata: notesDataset.endpointMetadata, + }, currentUserData: JSON.parse(notesDataset.currentUserData), notesData: JSON.parse(notesDataset.notesData), helpPagePath: notesDataset.helpPagePath, @@ -54,6 +57,7 @@ export default () => { }, created() { this.setActiveTab(window.mrTabs.getCurrentAction()); + this.setEndpoints(this.endpoints); }, mounted() { this.notesCountBadge = $('.issuable-details').find('.notes-tab .badge'); @@ -65,7 +69,7 @@ export default () => { window.mrTabs.eventHub.$off('MergeRequestTabChange', this.setActiveTab); }, methods: { - ...mapActions(['setActiveTab']), + ...mapActions(['setActiveTab', 'setEndpoints']), updateDiscussionTabCounter() { this.notesCountBadge.text(this.discussionTabCounter); }, diff --git a/app/assets/javascripts/mr_notes/stores/actions.js b/app/assets/javascripts/mr_notes/stores/actions.js index 426c6a00d5e..d1874dcb214 100644 --- a/app/assets/javascripts/mr_notes/stores/actions.js +++ b/app/assets/javascripts/mr_notes/stores/actions.js @@ -1,7 +1,9 @@ import types from './mutation_types'; -export default { - setActiveTab({ commit }, tab) { - commit(types.SET_ACTIVE_TAB, tab); - }, -}; +export function setActiveTab({ commit }, tab) { + commit(types.SET_ACTIVE_TAB, tab); +} + +export function setEndpoints({ commit }, endpoints) { + commit(types.SET_ENDPOINTS, endpoints); +} diff --git a/app/assets/javascripts/mr_notes/stores/modules/index.js b/app/assets/javascripts/mr_notes/stores/modules/index.js index c28e666943b..6e228c62a72 100644 --- a/app/assets/javascripts/mr_notes/stores/modules/index.js +++ b/app/assets/javascripts/mr_notes/stores/modules/index.js @@ -1,9 +1,10 @@ -import actions from '../actions'; +import * as actions from '../actions'; import getters from '../getters'; import mutations from '../mutations'; export default () => ({ state: { + endpoints: {}, activeTab: null, }, actions, diff --git a/app/assets/javascripts/mr_notes/stores/mutation_types.js b/app/assets/javascripts/mr_notes/stores/mutation_types.js index 105104361cf..67fa63f882d 100644 --- a/app/assets/javascripts/mr_notes/stores/mutation_types.js +++ b/app/assets/javascripts/mr_notes/stores/mutation_types.js @@ -1,3 +1,4 @@ export default { SET_ACTIVE_TAB: 'SET_ACTIVE_TAB', + SET_ENDPOINTS: 'SET_ENDPOINTS', }; diff --git a/app/assets/javascripts/mr_notes/stores/mutations.js b/app/assets/javascripts/mr_notes/stores/mutations.js index 8175aa9488f..3843103f4d0 100644 --- a/app/assets/javascripts/mr_notes/stores/mutations.js +++ b/app/assets/javascripts/mr_notes/stores/mutations.js @@ -4,4 +4,7 @@ export default { [types.SET_ACTIVE_TAB](state, tab) { Object.assign(state, { activeTab: tab }); }, + [types.SET_ENDPOINTS](state, endpoints) { + Object.assign(state, { endpoints }); + }, }; diff --git a/app/assets/javascripts/pages/admin/users/new/index.js b/app/assets/javascripts/pages/admin/users/new/index.js index 7b7d4c169ef..01710246c86 100644 --- a/app/assets/javascripts/pages/admin/users/new/index.js +++ b/app/assets/javascripts/pages/admin/users/new/index.js @@ -45,7 +45,5 @@ export default class UserInternalRegexHandler { } } -document.addEventListener('DOMContentLoaded', () => { - // eslint-disable-next-line - new UserInternalRegexHandler(); -}); +// eslint-disable-next-line no-new +new UserInternalRegexHandler(); diff --git a/app/assets/javascripts/pages/projects/jobs/show/index.js b/app/assets/javascripts/pages/projects/jobs/show/index.js index d57dbeb1242..6fef057dee0 100644 --- a/app/assets/javascripts/pages/projects/jobs/show/index.js +++ b/app/assets/javascripts/pages/projects/jobs/show/index.js @@ -1,3 +1,3 @@ import initJobDetails from '~/jobs'; -document.addEventListener('DOMContentLoaded', initJobDetails); +initJobDetails(); diff --git a/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue b/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue index f36b22f33c3..459580c86d6 100644 --- a/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue +++ b/app/assets/javascripts/pipeline_editor/components/editor/ci_config_merged_preview.vue @@ -82,7 +82,7 @@ export default { </gl-alert> <div v-else> <div class="gl-display-flex gl-align-items-center"> - <gl-icon :size="18" name="lock" use-deprecated-sizes class="gl-text-gray-500 gl-mr-3" /> + <gl-icon :size="16" name="lock" class="gl-text-gray-500 gl-mr-3" /> {{ $options.i18n.viewOnlyMessage }} </div> <div class="gl-mt-3 gl-border-solid gl-border-gray-100 gl-border-1"> diff --git a/app/assets/javascripts/repository/components/directory_download_links.vue b/app/assets/javascripts/repository/components/directory_download_links.vue index 8c029fc9973..c222a83300d 100644 --- a/app/assets/javascripts/repository/components/directory_download_links.vue +++ b/app/assets/javascripts/repository/components/directory_download_links.vue @@ -1,9 +1,9 @@ <script> -import { GlLink } from '@gitlab/ui'; +import { GlButton } from '@gitlab/ui'; export default { components: { - GlLink, + GlButton, }, props: { currentPath: { @@ -32,15 +32,15 @@ export default { <h5 class="m-0 dropdown-bold-header">{{ __('Download this directory') }}</h5> <div class="dropdown-menu-content"> <div class="btn-group ml-0 w-100"> - <gl-link + <gl-button v-for="(link, index) in normalizedLinks" :key="index" :href="link.path" - :class="{ 'btn-primary': index === 0 }" - class="btn btn-xs" + :variant="index === 0 ? 'confirm' : 'default'" + size="small" > {{ link.text }} - </gl-link> + </gl-button> </div> </div> </section> diff --git a/app/assets/stylesheets/page_bundles/boards.scss b/app/assets/stylesheets/page_bundles/boards.scss index fdf9e157e1e..61cbfec4597 100644 --- a/app/assets/stylesheets/page_bundles/boards.scss +++ b/app/assets/stylesheets/page_bundles/boards.scss @@ -40,8 +40,8 @@ [data-page$='epic_boards:index'], [data-page$='epic_boards:show'] { - .filter-form { - display: none; + .filtered-search-wrapper { + display: none !important; } } diff --git a/app/controllers/projects/registry/repositories_controller.rb b/app/controllers/projects/registry/repositories_controller.rb index 28a86ecc9f0..8acebd89033 100644 --- a/app/controllers/projects/registry/repositories_controller.rb +++ b/app/controllers/projects/registry/repositories_controller.rb @@ -6,22 +6,11 @@ module Projects include PackagesHelper before_action :authorize_update_container_image!, only: [:destroy] - before_action :ensure_root_container_repository!, only: [:index] def index respond_to do |format| - format.html - format.json do - @images = ContainerRepositoriesFinder.new(user: current_user, subject: project, params: params.slice(:name)) - .execute - - track_package_event(:list_repositories, :container) - - serializer = ContainerRepositoriesSerializer - .new(project: project, current_user: current_user) - - render json: serializer.with_pagination(request, response).represent(@images) - end + format.html { ensure_root_container_repository! } + format.json { render_404 } end end diff --git a/app/graphql/resolvers/group_milestones_resolver.rb b/app/graphql/resolvers/group_milestones_resolver.rb index 179283fd7b7..640c320dcfa 100644 --- a/app/graphql/resolvers/group_milestones_resolver.rb +++ b/app/graphql/resolvers/group_milestones_resolver.rb @@ -5,18 +5,37 @@ module Resolvers class GroupMilestonesResolver < MilestonesResolver argument :include_descendants, GraphQL::BOOLEAN_TYPE, required: false, - description: 'Also return milestones in all subgroups and subprojects.' + description: 'Include milestones from all subgroups and subprojects.' + argument :include_ancestors, GraphQL::BOOLEAN_TYPE, + required: false, + description: 'Include milestones from all parent groups.' type Types::MilestoneType.connection_type, null: true private def parent_id_parameters(args) - return { group_ids: parent.id } unless args[:include_descendants].present? + include_ancestors = args[:include_ancestors].present? + include_descendants = args[:include_descendants].present? + return { group_ids: parent.id } unless include_ancestors || include_descendants + + group_ids = if include_ancestors && include_descendants + parent.self_and_hierarchy + elsif include_ancestors + parent.self_and_ancestors + else + parent.self_and_descendants + end + + project_ids = if include_descendants + group_projects.with_issues_or_mrs_available_for_user(current_user) + else + nil + end { - group_ids: parent.self_and_descendants.public_or_visible_to_user(current_user).select(:id), - project_ids: group_projects.with_issues_or_mrs_available_for_user(current_user) + group_ids: group_ids.public_or_visible_to_user(current_user).select(:id), + project_ids: project_ids } end diff --git a/app/graphql/types/ci/job_type.rb b/app/graphql/types/ci/job_type.rb index c86337eea89..f2529b8e5c8 100644 --- a/app/graphql/types/ci/job_type.rb +++ b/app/graphql/types/ci/job_type.rb @@ -6,6 +6,8 @@ module Types graphql_name 'CiJob' authorize :read_commit_status + field :id, GraphQL::ID_TYPE, null: false, + description: 'ID of the job.' field :pipeline, Types::Ci::PipelineType, null: true, description: 'Pipeline the job belongs to.' field :name, GraphQL::STRING_TYPE, null: true, @@ -22,6 +24,8 @@ module Types description: 'When a job has finished running.' field :duration, GraphQL::INT_TYPE, null: true, description: 'Duration of the job in seconds.' + field :short_sha, type: GraphQL::STRING_TYPE, null: false, + description: 'Short SHA1 ID of the commit.' def pipeline Gitlab::Graphql::Loaders::BatchModelLoader.new(::Ci::Pipeline, object.pipeline_id).find diff --git a/app/models/experiment.rb b/app/models/experiment.rb index ac8b6516d02..7ffb321f2b7 100644 --- a/app/models/experiment.rb +++ b/app/models/experiment.rb @@ -21,7 +21,13 @@ class Experiment < ApplicationRecord # Create or update the recorded experiment_user row for the user in this experiment. def record_user_and_group(user, group_type, context = {}) experiment_user = experiment_users.find_or_initialize_by(user: user) - experiment_user.update!(group_type: group_type, context: merged_context(experiment_user, context)) + experiment_user.assign_attributes(group_type: group_type, context: merged_context(experiment_user, context)) + # We only call save when necessary because this causes the request to stick to the primary DB + # even when the save is a no-op + # https://gitlab.com/gitlab-org/gitlab/-/issues/324649 + experiment_user.save! if experiment_user.changed? + + experiment_user end def record_conversion_event_for_user(user, context = {}) @@ -32,7 +38,14 @@ class Experiment < ApplicationRecord end def record_group_and_variant!(group, variant) - experiment_subjects.find_or_initialize_by(group: group).update!(variant: variant) + experiment_subject = experiment_subjects.find_or_initialize_by(group: group) + experiment_subject.assign_attributes(variant: variant) + # We only call save when necessary because this causes the request to stick to the primary DB + # even when the save is a no-op + # https://gitlab.com/gitlab-org/gitlab/-/issues/324649 + experiment_subject.save! if experiment_subject.changed? + + experiment_subject end private diff --git a/app/services/issuable_links/create_service.rb b/app/services/issuable_links/create_service.rb index f148c503dcf..4816be566dd 100644 --- a/app/services/issuable_links/create_service.rb +++ b/app/services/issuable_links/create_service.rb @@ -107,11 +107,11 @@ module IssuableLinks end def issuables_assigned_message - 'Issue(s) already assigned' + _("Issue(s) already assigned") end def issuables_not_found_message - 'No Issue found for given params' + _("No matching issue found. Make sure that you are adding a valid issue URL.") end end end diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index 1f09f3097ad..82bd5c095a8 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -28,7 +28,7 @@ = link_to _('Manage two-factor authentication'), profile_two_factor_auth_path, class: 'gl-button btn btn-confirm' - else .gl-mb-3 - = link_to _('Enable two-factor authentication'), profile_two_factor_auth_path, class: 'gl-button btn btn-success', data: { qa_selector: 'enable_2fa_button' } + = link_to _('Enable two-factor authentication'), profile_two_factor_auth_path, class: 'gl-button btn btn-confirm', data: { qa_selector: 'enable_2fa_button' } .col-lg-12 %hr diff --git a/app/views/profiles/chat_names/new.html.haml b/app/views/profiles/chat_names/new.html.haml index 4651854a551..f008abf376d 100644 --- a/app/views/profiles/chat_names/new.html.haml +++ b/app/views/profiles/chat_names/new.html.haml @@ -8,7 +8,7 @@ .actions = form_tag profile_chat_names_path, method: :post do = hidden_field_tag :token, @chat_name_token.token - = submit_tag _("Authorize"), class: "gl-button btn btn-success wide float-left" + = submit_tag _("Authorize"), class: "gl-button btn btn-confirm wide float-left" = form_tag deny_profile_chat_names_path, method: :delete do = hidden_field_tag :token, @chat_name_token.token = submit_tag _("Deny"), class: "gl-button btn btn-danger gl-ml-3" diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml index 89198b0a65b..d78b542ae8a 100644 --- a/app/views/profiles/emails/index.html.haml +++ b/app/views/profiles/emails/index.html.haml @@ -15,7 +15,7 @@ = f.label :email, _('Email'), class: 'label-bold' = f.text_field :email, class: 'form-control gl-form-input', data: { qa_selector: 'email_address_field' } .gl-mt-3 - = f.submit _('Add email address'), class: 'gl-button btn btn-success', data: { qa_selector: 'add_email_address_button' } + = f.submit _('Add email address'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'add_email_address_button' } %hr %h4.gl-mt-0 = _('Linked emails (%{email_count})') % { email_count: @emails.load.size + 1 } diff --git a/app/views/profiles/gpg_keys/_form.html.haml b/app/views/profiles/gpg_keys/_form.html.haml index 7a7b5802cd8..a86de0681f7 100644 --- a/app/views/profiles/gpg_keys/_form.html.haml +++ b/app/views/profiles/gpg_keys/_form.html.haml @@ -7,4 +7,4 @@ = f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: _("Don't paste the private part of the GPG key. Paste the public part which begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----'.") .gl-mt-3 - = f.submit s_('Profiles|Add key'), class: "gl-button btn btn-success" + = f.submit s_('Profiles|Add key'), class: "gl-button btn btn-confirm" diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml index 81a543de7a3..96a05097935 100644 --- a/app/views/profiles/keys/_form.html.haml +++ b/app/views/profiles/keys/_form.html.haml @@ -21,7 +21,7 @@ %strong= _('Oops, are you sure?') %p= s_("Profiles|This doesn't look like a public SSH key, are you sure you want to add it? It will be publicly visible.") - %button.btn.gl-button.btn-success.js-add-ssh-key-validation-confirm-submit= _("Yes, add it") + %button.btn.gl-button.btn-confirm.js-add-ssh-key-validation-confirm-submit= _("Yes, add it") .gl-mt-3 - = f.submit s_('Profiles|Add key'), class: "gl-button btn btn-success js-add-ssh-key-validation-original-submit qa-add-key-button" + = f.submit s_('Profiles|Add key'), class: "gl-button btn btn-confirm js-add-ssh-key-validation-original-submit qa-add-key-button" diff --git a/app/views/profiles/passwords/edit.html.haml b/app/views/profiles/passwords/edit.html.haml index b281dbb4367..2cc919fc70e 100644 --- a/app/views/profiles/passwords/edit.html.haml +++ b/app/views/profiles/passwords/edit.html.haml @@ -30,6 +30,6 @@ = f.label :password_confirmation, _('Password confirmation'), class: 'label-bold' = f.password_field :password_confirmation, required: true, class: 'form-control gl-form-input', data: { qa_selector: 'confirm_password_field' } .gl-mt-3.gl-mb-3 - = f.submit _('Save password'), class: "gl-button btn btn-success gl-mr-3", data: { qa_selector: 'save_password_button' } + = f.submit _('Save password'), class: "gl-button btn btn-confirm gl-mr-3", data: { qa_selector: 'save_password_button' } - unless @user.password_automatically_set? = link_to _('I forgot my password'), reset_profile_password_path, method: :put diff --git a/app/views/profiles/passwords/new.html.haml b/app/views/profiles/passwords/new.html.haml index ffec6baa20e..efcd0bb621f 100644 --- a/app/views/profiles/passwords/new.html.haml +++ b/app/views/profiles/passwords/new.html.haml @@ -28,4 +28,4 @@ .col-sm-10 = f.password_field :password_confirmation, required: true, class: 'form-control gl-form-input' .form-actions - = f.submit _('Set new password'), class: 'gl-button btn btn-success' + = f.submit _('Set new password'), class: 'gl-button btn btn-confirm' diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 7995231c739..cc5ce00669e 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -33,7 +33,7 @@ %h5.gl-mt-0= s_("Profiles|Upload new avatar") .gl-mt-2.gl-mb-3 %button.gl-button.btn.js-choose-user-avatar-button{ type: 'button' }= s_("Profiles|Choose file...") - %span.avatar-file-name.gl-ml-3.js-avatar-filename= s_("Profiles|No file chosen") + %span.avatar-file-name.gl-ml-3.js-avatar-filename= s_("Profiles|No file chosen.") = f.file_field_without_bootstrap :avatar, class: 'js-user-avatar-input hidden', accept: 'image/*' .form-text.text-muted= s_("Profiles|The maximum file size allowed is 200KB.") - if @user.avatar? @@ -127,7 +127,7 @@ = s_("Profiles|Choose to show contributions of private projects on your public profile without any project, repository or organization information") .row.gl-mt-3.gl-mb-3.gl-justify-content-end .col-lg-8 - = f.submit s_("Profiles|Update profile settings"), class: 'gl-button btn btn-success' + = f.submit s_("Profiles|Update profile settings"), class: 'gl-button btn btn-confirm' = link_to _("Cancel"), user_path(current_user), class: 'gl-button btn btn-cancel' .modal.modal-profile-crop{ data: { cropper_css_path: ActionController::Base.helpers.stylesheet_path('lazy_bundles/cropper.css') } } diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml index 3853f428447..a9134057777 100644 --- a/app/views/profiles/two_factor_auths/show.html.haml +++ b/app/views/profiles/two_factor_auths/show.html.haml @@ -51,7 +51,7 @@ = label_tag :pin_code, _('Pin code'), class: "label-bold" = text_field_tag :pin_code, nil, class: "form-control", required: true, data: { qa_selector: 'pin_code_field' } .gl-mt-3 - = submit_tag _('Register with two-factor app'), class: 'gl-button btn btn-success', data: { qa_selector: 'register_2fa_app_button' } + = submit_tag _('Register with two-factor app'), class: 'gl-button btn btn-confirm', data: { qa_selector: 'register_2fa_app_button' } %hr diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml index 5da9c25b780..06a2ed46805 100644 --- a/app/views/projects/environments/index.html.haml +++ b/app/views/projects/environments/index.html.haml @@ -6,4 +6,5 @@ "can-create-environment" => can?(current_user, :create_environment, @project).to_s, "new-environment-path" => new_project_environment_path(@project), "help-page-path" => help_page_path("ci/environments/index.md"), - "project-path" => @project.full_path } } + "project-path" => @project.full_path, + "default-branch-name" => @project.default_branch_or_master } } diff --git a/app/views/projects/merge_requests/creations/_new_compare.html.haml b/app/views/projects/merge_requests/creations/_new_compare.html.haml index 2cb75d43d4b..c7861f4a01a 100644 --- a/app/views/projects/merge_requests/creations/_new_compare.html.haml +++ b/app/views/projects/merge_requests/creations/_new_compare.html.haml @@ -64,4 +64,4 @@ - if @merge_request.errors.any? = form_errors(@merge_request) - = f.submit 'Compare branches and continue', class: "gl-button btn btn-success mr-compare-btn" + = f.submit 'Compare branches and continue', class: "gl-button btn btn-confirm mr-compare-btn" diff --git a/app/views/projects/merge_requests/show.html.haml b/app/views/projects/merge_requests/show.html.haml index f0dcaf24e07..36491b0d8b8 100644 --- a/app/views/projects/merge_requests/show.html.haml +++ b/app/views/projects/merge_requests/show.html.haml @@ -13,6 +13,8 @@ - add_page_specific_style 'page_bundles/reports' - add_page_specific_style 'page_bundles/ci_status' +- add_page_startup_api_call @endpoint_metadata_url + .merge-request{ data: { mr_action: mr_action, url: merge_request_path(@merge_request, format: :json), project_path: project_path(@merge_request.project), lock_version: @merge_request.lock_version } } = render "projects/merge_requests/mr_title" @@ -63,6 +65,7 @@ - add_page_startup_api_call widget_project_json_merge_request_path(@project, @merge_request, format: :json) - add_page_startup_api_call cached_widget_project_json_merge_request_path(@project, @merge_request, format: :json) #js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request, Feature.enabled?(:paginated_notes, @project)).to_json, + endpoint_metadata: @endpoint_metadata_url, noteable_data: serialize_issuable(@merge_request, serializer: 'noteable'), noteable_type: 'MergeRequest', target_type: 'merge_request', @@ -75,8 +78,6 @@ = render "projects/merge_requests/tabs/pane", name: "pipelines", id: "pipelines", class: "pipelines" do - if number_of_pipelines.nonzero? = render 'projects/commit/pipelines_list', disable_initialization: true, endpoint: pipelines_project_merge_request_path(@project, @merge_request) - - if mr_action === "diffs" - - add_page_startup_api_call @endpoint_metadata_url - params = request.query_parameters - if Feature.enabled?(:default_merge_ref_for_diffs, @project, default_enabled: :yaml) - params = params.merge(diff_head: true) diff --git a/app/views/shared/_file_picker_button.html.haml b/app/views/shared/_file_picker_button.html.haml index 9e6a7626d89..a30c144c38f 100644 --- a/app/views/shared/_file_picker_button.html.haml +++ b/app/views/shared/_file_picker_button.html.haml @@ -2,7 +2,7 @@ %span.js-filepicker %button.gl-button.btn.js-filepicker-button{ type: 'button', class: classes }= _("Choose file…") - %span.file_name.js-filepicker-filename= _("No file chosen") + %span.file_name.js-filepicker-filename= _("No file chosen.") = f.file_field field, class: "js-filepicker-input hidden" - if help_text.present? .form-text.text-muted= help_text diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index f5b2868aa6c..4233dfec698 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -5,7 +5,8 @@ - placeholder = local_assigns[:placeholder] || _('Search or filter results...') - is_not_boards_modal_or_productivity_analytics = type != :boards_modal && type != :productivity_analytics - block_css_class = is_not_boards_modal_or_productivity_analytics ? 'row-content-block second-block' : '' -- if board && board.to_type == "EpicBoard" +- is_epic_board = board&.to_type == "EpicBoard" +- if is_epic_board - user_can_admin_list = can?(current_user, :admin_epic_board_list, board.resource_parent) - elsif board - user_can_admin_list = can?(current_user, :admin_issue_board_list, board.resource_parent) @@ -21,7 +22,7 @@ - if @can_bulk_update .check-all-holder.d-none.d-sm-block.hidden = check_box_tag "check-all-issues", nil, false, class: "check-all-issues left" - - if Feature.enabled?(:boards_filtered_search, @group) + - if Feature.enabled?(:boards_filtered_search, @group) && is_epic_board #js-board-filtered-search - else .issues-other-filters.filtered-search-wrapper.d-flex.flex-column.flex-md-row diff --git a/changelogs/unreleased/273283-update-compare-page-button.yml b/changelogs/unreleased/273283-update-compare-page-button.yml new file mode 100644 index 00000000000..37e48e386fe --- /dev/null +++ b/changelogs/unreleased/273283-update-compare-page-button.yml @@ -0,0 +1,5 @@ +--- +title: Update compare branches button to btn-confirm +merge_request: 56791 +author: +type: changed diff --git a/changelogs/unreleased/323433-add-includeancestors-to-group-milestones-graphql.yml b/changelogs/unreleased/323433-add-includeancestors-to-group-milestones-graphql.yml new file mode 100644 index 00000000000..56cd6538bc6 --- /dev/null +++ b/changelogs/unreleased/323433-add-includeancestors-to-group-milestones-graphql.yml @@ -0,0 +1,5 @@ +--- +title: Support include_ancestors when querying group milestones via GraphQL +merge_request: 56667 +author: +type: added diff --git a/changelogs/unreleased/324100-monitor.yml b/changelogs/unreleased/324100-monitor.yml new file mode 100644 index 00000000000..90929823825 --- /dev/null +++ b/changelogs/unreleased/324100-monitor.yml @@ -0,0 +1,5 @@ +--- +title: Update master to main inside monitor copy +merge_request: 56264 +author: +type: changed diff --git a/changelogs/unreleased/324649-prevent-experiments-sticking-to-primary.yml b/changelogs/unreleased/324649-prevent-experiments-sticking-to-primary.yml new file mode 100644 index 00000000000..4657e2f0a81 --- /dev/null +++ b/changelogs/unreleased/324649-prevent-experiments-sticking-to-primary.yml @@ -0,0 +1,5 @@ +--- +title: Prevent sticking to DB primary when experiments are tracked +merge_request: 56852 +author: +type: performance diff --git a/changelogs/unreleased/btn-confirm-download-dir.yml b/changelogs/unreleased/btn-confirm-download-dir.yml new file mode 100644 index 00000000000..5d438939d17 --- /dev/null +++ b/changelogs/unreleased/btn-confirm-download-dir.yml @@ -0,0 +1,5 @@ +--- +title: Move to btn-confirm in download directory dropdown +merge_request: 56193 +author: Yogi (@yo) +type: changed diff --git a/changelogs/unreleased/btn-confirm-feature-flags.yml b/changelogs/unreleased/btn-confirm-feature-flags.yml new file mode 100644 index 00000000000..999c042845b --- /dev/null +++ b/changelogs/unreleased/btn-confirm-feature-flags.yml @@ -0,0 +1,5 @@ +--- +title: Move to confirm variant from success in feature_flags directory +merge_request: 56202 +author: Yogi (@yo) +type: changed diff --git a/changelogs/unreleased/btn-confirm-user-settings.yml b/changelogs/unreleased/btn-confirm-user-settings.yml new file mode 100644 index 00000000000..2762b2db139 --- /dev/null +++ b/changelogs/unreleased/btn-confirm-user-settings.yml @@ -0,0 +1,5 @@ +--- +title: Move from btn-success to btn-confirm in app/views/profiles directory +merge_request: 54748 +author: Yogi (@yo) +type: changed diff --git a/changelogs/unreleased/jivanvl-add-fields-to-job-type-graphql.yml b/changelogs/unreleased/jivanvl-add-fields-to-job-type-graphql.yml new file mode 100644 index 00000000000..35d5b737b6b --- /dev/null +++ b/changelogs/unreleased/jivanvl-add-fields-to-job-type-graphql.yml @@ -0,0 +1,5 @@ +--- +title: Add id and short_sha GraphQL fields to jobType in the CI namespace +merge_request: 56714 +author: +type: changed diff --git a/changelogs/unreleased/jivanvl-fix-icon-size-pipeline-editor.yml b/changelogs/unreleased/jivanvl-fix-icon-size-pipeline-editor.yml new file mode 100644 index 00000000000..211a95643b0 --- /dev/null +++ b/changelogs/unreleased/jivanvl-fix-icon-size-pipeline-editor.yml @@ -0,0 +1,5 @@ +--- +title: Change icon size in the pipeline editor +merge_request: 56780 +author: +type: changed diff --git a/changelogs/unreleased/kassio-phabricator-enabled-by-default.yml b/changelogs/unreleased/kassio-phabricator-enabled-by-default.yml new file mode 100644 index 00000000000..0a4234f8987 --- /dev/null +++ b/changelogs/unreleased/kassio-phabricator-enabled-by-default.yml @@ -0,0 +1,5 @@ +--- +title: Enabled phabricator importer by default +merge_request: 56765 +author: +type: added diff --git a/changelogs/unreleased/mjang-okr-issue300633.yml b/changelogs/unreleased/mjang-okr-issue300633.yml new file mode 100644 index 00000000000..f73a8e0a7ac --- /dev/null +++ b/changelogs/unreleased/mjang-okr-issue300633.yml @@ -0,0 +1,5 @@ +--- +title: Update UI text to match style guidelines +merge_request: +author: +type: other diff --git a/changelogs/unreleased/msjr-no-issue-found-for-params.yml b/changelogs/unreleased/msjr-no-issue-found-for-params.yml new file mode 100644 index 00000000000..df314f51411 --- /dev/null +++ b/changelogs/unreleased/msjr-no-issue-found-for-params.yml @@ -0,0 +1,5 @@ +--- +title: Resolve Improve text for error No issue found for given params in UI +merge_request: 45064 +author: +type: other diff --git a/changelogs/unreleased/remove-namespace_project_container_registry_index-json.yml b/changelogs/unreleased/remove-namespace_project_container_registry_index-json.yml new file mode 100644 index 00000000000..54cfd8a902f --- /dev/null +++ b/changelogs/unreleased/remove-namespace_project_container_registry_index-json.yml @@ -0,0 +1,5 @@ +--- +title: Remove JSON endpoint for project container index +merge_request: 52407 +author: Takuya Noguchi +type: other diff --git a/config/feature_flags/development/phabricator_import.yml b/config/feature_flags/development/phabricator_import.yml index 264988e8006..5340caef140 100644 --- a/config/feature_flags/development/phabricator_import.yml +++ b/config/feature_flags/development/phabricator_import.yml @@ -5,4 +5,4 @@ rollout_issue_url: https://gitlab.com/groups/gitlab-org/-/epics/1197 milestone: '12.0' type: development group: group::import -default_enabled: false +default_enabled: true diff --git a/config/initializers/kramdown_patch.rb b/config/initializers/kramdown_patch.rb new file mode 100644 index 00000000000..5cb769cec24 --- /dev/null +++ b/config/initializers/kramdown_patch.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true +# +# This pulls in https://github.com/gettalong/kramdown/pull/708 for kramdown v2.3.0. +# Remove this file when that pull request is merged and released. +require 'kramdown/converter' +require 'kramdown/converter/syntax_highlighter/rouge' + +module Kramdown::Converter::SyntaxHighlighter + module Rouge + def self.formatter_class(opts = {}) + case formatter = opts[:formatter] + when Class + formatter + when /\A[[:upper:]][[:alnum:]_]*\z/ + ::Rouge::Formatters.const_get(formatter, false) + else + # Available in Rouge 2.0 or later + ::Rouge::Formatters::HTMLLegacy + end + rescue NameError + # Fallback to Rouge 1.x + ::Rouge::Formatters::HTML + end + end +end diff --git a/db/structure.sql b/db/structure.sql index 29c3e973f4c..0c4bd0d4ff6 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -9432,39 +9432,39 @@ CREATE TABLE application_settings ( elasticsearch_indexed_file_size_limit_kb integer DEFAULT 1024 NOT NULL, enforce_namespace_storage_limit boolean DEFAULT false NOT NULL, container_registry_delete_tags_service_timeout integer DEFAULT 250 NOT NULL, - kroki_url character varying, - kroki_enabled boolean, - elasticsearch_client_request_timeout integer DEFAULT 0 NOT NULL, gitpod_enabled boolean DEFAULT false NOT NULL, gitpod_url text DEFAULT 'https://gitpod.io/'::text, + elasticsearch_client_request_timeout integer DEFAULT 0 NOT NULL, abuse_notification_email character varying, require_admin_approval_after_user_signup boolean DEFAULT true NOT NULL, help_page_documentation_base_url text, automatic_purchased_storage_allocation boolean DEFAULT false NOT NULL, + container_registry_expiration_policies_worker_capacity integer DEFAULT 0 NOT NULL, encrypted_ci_jwt_signing_key text, encrypted_ci_jwt_signing_key_iv text, - container_registry_expiration_policies_worker_capacity integer DEFAULT 0 NOT NULL, - elasticsearch_analyzers_smartcn_enabled boolean DEFAULT false NOT NULL, - elasticsearch_analyzers_smartcn_search boolean DEFAULT false NOT NULL, - elasticsearch_analyzers_kuromoji_enabled boolean DEFAULT false NOT NULL, - elasticsearch_analyzers_kuromoji_search boolean DEFAULT false NOT NULL, secret_detection_token_revocation_enabled boolean DEFAULT false NOT NULL, secret_detection_token_revocation_url text, encrypted_secret_detection_token_revocation_token text, encrypted_secret_detection_token_revocation_token_iv text, + elasticsearch_analyzers_smartcn_enabled boolean DEFAULT false NOT NULL, + elasticsearch_analyzers_smartcn_search boolean DEFAULT false NOT NULL, + elasticsearch_analyzers_kuromoji_enabled boolean DEFAULT false NOT NULL, + elasticsearch_analyzers_kuromoji_search boolean DEFAULT false NOT NULL, + new_user_signups_cap integer, domain_denylist_enabled boolean DEFAULT false, domain_denylist text, domain_allowlist text, - new_user_signups_cap integer, encrypted_cloud_license_auth_token text, encrypted_cloud_license_auth_token_iv text, secret_detection_revocation_token_types_url text, cloud_license_enabled boolean DEFAULT false NOT NULL, + kroki_url text, + kroki_enabled boolean DEFAULT false NOT NULL, disable_feed_token boolean DEFAULT false NOT NULL, personal_access_token_prefix text, rate_limiting_response_text text, - invisible_captcha_enabled boolean DEFAULT false NOT NULL, container_registry_cleanup_tags_service_max_list_size integer DEFAULT 200 NOT NULL, + invisible_captcha_enabled boolean DEFAULT false NOT NULL, enforce_ssh_key_expiration boolean DEFAULT false NOT NULL, git_two_factor_session_expiry integer DEFAULT 15 NOT NULL, keep_latest_artifact boolean DEFAULT true NOT NULL, @@ -9475,7 +9475,7 @@ CREATE TABLE application_settings ( asset_proxy_whitelist text, CONSTRAINT app_settings_container_reg_cleanup_tags_max_list_size_positive CHECK ((container_registry_cleanup_tags_service_max_list_size >= 0)), CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)), - CONSTRAINT check_17d9558205 CHECK ((char_length((kroki_url)::text) <= 1024)), + CONSTRAINT check_17d9558205 CHECK ((char_length(kroki_url) <= 1024)), CONSTRAINT check_2dba05b802 CHECK ((char_length(gitpod_url) <= 255)), CONSTRAINT check_51700b31b5 CHECK ((char_length(default_branch_name) <= 255)), CONSTRAINT check_57123c9593 CHECK ((char_length(help_page_documentation_base_url) <= 255)), @@ -14675,7 +14675,8 @@ CREATE TABLE namespaces ( shared_runners_enabled boolean DEFAULT true NOT NULL, allow_descendants_override_disabled_shared_runners boolean DEFAULT false NOT NULL, traversal_ids integer[] DEFAULT '{}'::integer[] NOT NULL, - delayed_project_removal boolean DEFAULT false NOT NULL + delayed_project_removal boolean DEFAULT false NOT NULL, + resource_access_tokens_enabled boolean DEFAULT true NOT NULL ); CREATE SEQUENCE namespaces_id_seq diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 496fe178c77..3c258841607 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -1198,10 +1198,12 @@ An edge in a connection. | `detailedStatus` | [`DetailedStatus`](#detailedstatus) | Detailed status of the job. | | `duration` | [`Int`](#int) | Duration of the job in seconds. | | `finishedAt` | [`Time`](#time) | When a job has finished running. | +| `id` | [`ID!`](#id) | ID of the job. | | `name` | [`String`](#string) | Name of the job. | | `needs` | [`CiBuildNeedConnection`](#cibuildneedconnection) | References to builds that must complete before the jobs run. | | `pipeline` | [`Pipeline`](#pipeline) | Pipeline the job belongs to. | | `scheduledAt` | [`Time`](#time) | Schedule for the build. | +| `shortSha` | [`String!`](#string) | Short SHA1 ID of the commit. | ### `CiJobArtifact` diff --git a/doc/integration/vault.md b/doc/integration/vault.md index 362ae36389b..06f8a452427 100644 --- a/doc/integration/vault.md +++ b/doc/integration/vault.md @@ -76,15 +76,25 @@ The following assumes you already have Vault installed and running. This configuration is saved under the name of the role you are creating. In this case, we are creating a `demo` role. Later, we show how you can access this role through the Vault CLI. + WARNING: + If you're using a public GitLab instance (GitLab.com or any other instance publicly + accessible), it's paramount to specify the `bound_claims` to allow access only to + members of your group/project. Otherwise, anyone with a public account can access + your Vault instance. + ```shell - vault write auth/oidc/role/demo \ - user_claim="sub" \ - allowed_redirect_uris="http://localhost:8250/oidc/callback,http://127.0.0.1:8200/ui/vault/auth/oidc/oidc/callback" \ - bound_audiences="your_application_id" \ - role_type="oidc" \ - oidc_scopes="openid" \ - policies=demo \ - ttl=1h + vault write auth/oidc/role/demo -<<EOF + { + "user_claim": "sub", + "allowed_redirect_uris": "your_vault_instance_redirect_uris", + "bound_audiences": "your_application_id", + "oidc_scopes": "openid", + "role_type": "oidc", + "policies": "demo", + "ttl": "1h", + "bound_claims": { "groups": ["yourGroup/yourSubgrup"] } + } + EOF ``` 1. **Sign in to Vault:** diff --git a/doc/user/admin_area/credentials_inventory.md b/doc/user/admin_area/credentials_inventory.md index 053cee82634..a75c3517027 100644 --- a/doc/user/admin_area/credentials_inventory.md +++ b/doc/user/admin_area/credentials_inventory.md @@ -56,10 +56,14 @@ The instance then notifies the user. ## Review existing GPG keys > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/282429) in GitLab 13.10. -> - It's [deployed behind a feature flag](../feature_flags.md), disabled by default. -> - It's disabled on GitLab.com. -> - It's not recommended for production use. -> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-the-gpg-keys-view). +> - It was [deployed behind a feature flag](../feature_flags.md), disabled by default. +> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/292961) on GitLab 13.11. +> - It's enabled on GitLab.com. +> - It's recommended for production use. +> - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-the-gpg-keys-view). + +WARNING: +This feature might not be available to you. Check the **version history** note above for details. You can view all existing GPG in your GitLab instance by navigating to the credentials inventory GPG Keys tab, as well as the following properties: @@ -72,10 +76,10 @@ credentials inventory GPG Keys tab, as well as the following properties: ### Enable or disable the GPG keys view -Enabling or disabling the GPG keys view is under development and not ready for production use. It is -deployed behind a feature flag that is **disabled by default**. +Enabling or disabling the GPG keys view is under development but ready for production use. +It is deployed behind a feature flag that is **enabled by default**. [GitLab administrators with access to the GitLab Rails console](../../administration/feature_flags.md) -can enable it. +can opt to disable it. To enable it: diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index f87ea8edc7b..f82b5be2abc 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -183,10 +183,11 @@ The following variables are used for configuring specific analyzers (used for a | `BUNDLER_AUDIT_ADVISORY_DB_URL` | `bundler-audit` | `https://github.com/rubysec/ruby-advisory-db` | URL of the advisory database used by bundler-audit. | | `BUNDLER_AUDIT_ADVISORY_DB_REF_NAME` | `bundler-audit` | `master` | Git ref for the advisory database specified by `BUNDLER_AUDIT_ADVISORY_DB_URL`. | | `GEMNASIUM_DB_LOCAL_PATH` | `gemnasium` | `/gemnasium-db` | Path to local Gemnasium database. | +| `GEMNASIUM_DB_UPDATE_DISABLED` | `gemnasium` | `"false"` | Disable automatic updates for the `gemnasium-db` advisory database (For usage see: [examples](#hosting-a-copy-of-the-gemnasium_db-advisory-database))| | `GEMNASIUM_DB_REMOTE_URL` | `gemnasium` | `https://gitlab.com/gitlab-org/security-products/gemnasium-db.git` | Repository URL for fetching the Gemnasium database. | | `GEMNASIUM_DB_REF_NAME` | `gemnasium` | `master` | Branch name for remote repository database. `GEMNASIUM_DB_REMOTE_URL` is required. | | `DS_REMEDIATE` | `gemnasium` | `"true"` | Enable automatic remediation of vulnerable dependencies. | -| `DS_JAVA_VERSION` | `gemnasium-maven` | `11` | Version of Java. Available versions: `8`, `11`, `13`, `14`. Maven and Gradle use the Java version specified by this value. | +| `DS_JAVA_VERSION` | `gemnasium-maven` | `11` | Version of Java. Available versions: `8`, `11`, `13`, `14`, `15`. Maven and Gradle use the Java version specified by this value. | | `MAVEN_CLI_OPTS` | `gemnasium-maven` | `"-DskipTests --batch-mode"` | List of command line arguments that are passed to `maven` by the analyzer. See an example for [using private repositories](../index.md#using-private-maven-repositories). | | `GRADLE_CLI_OPTS` | `gemnasium-maven` | | List of command line arguments that are passed to `gradle` by the analyzer. | | `SBT_CLI_OPTS` | `gemnasium-maven` | | List of command-line arguments that the analyzer passes to `sbt`. | @@ -505,6 +506,50 @@ ensure that it can reach your private repository. Here is an example configurati setuptools.ssl_support.cert_paths = ['internal.crt'] ``` +## Hosting a copy of the gemnasium_db advisory database + +The [gemnasium_db](https://gitlab.com/gitlab-org/security-products/gemnasium-db) Git repository is +used by `gemnasium`, `gemnasium-maven`, and `gemnasium-python` as the source of vulnerability data. +This repository updates at scan time to fetch the latest advisories. However, due to a restricted +networking environment, running this update is sometimes not possible. In this case, a user can do +one of the following: + +- [Host a copy of the advisory database](#host-a-copy-of-the-advisory-database) +- [Use a local clone](#use-a-local-clone) + +### Host a copy of the advisory database + +If [gemnasium-db](https://gitlab.com/gitlab-org/security-products/gemnasium-db) is not reachable +from within the environment, the user can host their own Git copy. Then the analyzer can be +instructed to update the database from the user's copy by using `GEMNASIUM_DB_REMOTE_URL`: + +```yaml +variables: + GEMNASIUM_DB_REMOTE_URL: https://users-own-copy.example.com/gemnasium-db/.git + +... +``` + +### Use a local clone + +If a hosted copy is not possible, then the user can clone [gemnasium-db](https://gitlab.com/gitlab-org/security-products/gemnasium-db) +or create an archive before the scan and point the analyzer to the directory (using: +`GEMNASIUM_DB_LOCAL_PATH`). Turn off the analyzer's self-update mechanism (using: +`GEMNASIUM_DB_UPDATE_DISABLED`). In this example, the database directory is created in the +`before_script`, before the `gemnasium` analyzer's scan job: + +```yaml +... + +gemnasium-dependency_scanning: + variables: + GEMNASIUM_DB_LOCAL_PATH: ./gemnasium-db-local + GEMNASIUM_DB_UPDATE_DISABLED: "true" + before_script: + - mkdir $GEMNASIUM_DB_LOCAL_PATH + - tar -xzf gemnasium_db.tar.gz -C $GEMNASIUM_DB_LOCAL_PATH +``` + ## Limitations ### Referencing local dependencies using a path in JavaScript projects diff --git a/doc/user/group/saml_sso/scim_setup.md b/doc/user/group/saml_sso/scim_setup.md index 35374812b37..e995a6c4e72 100644 --- a/doc/user/group/saml_sso/scim_setup.md +++ b/doc/user/group/saml_sso/scim_setup.md @@ -168,6 +168,10 @@ As the app is developed by OneLogin, please reach out to OneLogin if you encount ## User access and linking setup +During the synchronization process, all of your users get GitLab accounts, welcoming them +to their respective groups, with an invitation email. When implementing SCIM provisioning, +you may want to warn your security-conscious employees about this email. + The following diagram is a general outline on what happens when you add users to your SCIM app: ```mermaid @@ -202,10 +206,6 @@ Upon the next sync, the user is deprovisioned, which means that the user is remo NOTE: Deprovisioning does not delete the user account. -During the synchronization process, all of your users get GitLab accounts, welcoming them -to their respective groups, with an invitation email. When implementing SCIM provisioning, -you may want to warn your security-conscious employees about this email. - ```mermaid graph TD A[Remove User from SCIM app] -->|IdP sends request to GitLab| B(GitLab: Is the user part of the group?) diff --git a/doc/user/project/import/phabricator.md b/doc/user/project/import/phabricator.md index 189afac1226..91fd7b8928b 100644 --- a/doc/user/project/import/phabricator.md +++ b/doc/user/project/import/phabricator.md @@ -36,13 +36,4 @@ of the project being imported into, then the user will be linked. ## Enabling this feature -While this feature is incomplete, a feature flag is required to enable it so that -we can gain early feedback before releasing it for everyone. To enable it: - -1. Run the following command in a Rails console: - - ```ruby - Feature.enable(:phabricator_import) - ``` - -1. Enable Phabricator as an [import source](../../admin_area/settings/visibility_and_access_controls.md#import-sources) in the Admin Area. +Enable Phabricator as an [import source](../../admin_area/settings/visibility_and_access_controls.md#import-sources) in the Admin Area. diff --git a/lib/gitlab/phabricator_import.rb b/lib/gitlab/phabricator_import.rb index 3885a9934d5..4c9d54a93ce 100644 --- a/lib/gitlab/phabricator_import.rb +++ b/lib/gitlab/phabricator_import.rb @@ -5,7 +5,7 @@ module Gitlab BaseError = Class.new(StandardError) def self.available? - Feature.enabled?(:phabricator_import) && + Feature.enabled?(:phabricator_import, default_enabled: :yaml) && Gitlab::CurrentSettings.import_sources.include?('phabricator') end end diff --git a/lib/gitlab/regex.rb b/lib/gitlab/regex.rb index 00739c05386..1082d63724c 100644 --- a/lib/gitlab/regex.rb +++ b/lib/gitlab/regex.rb @@ -385,11 +385,11 @@ module Gitlab end def merge_request_wip - /(?i)(\[WIP\]\s*|WIP:\s*|WIP$)/ + /(?i)(\[WIP\]\s*|WIP:\s*|\AWIP\z)/ end def merge_request_draft - /(?i)(\[draft\]|\(draft\)|draft:|draft\s\-\s|draft$)/ + /\A(?i)(\[draft\]|\(draft\)|draft:|draft\s\-\s|draft\z)/ end def issue diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 8e147f5d143..96c0cbcf327 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -17073,6 +17073,9 @@ msgstr "" msgid "Issue weight" msgstr "" +msgid "Issue(s) already assigned" +msgstr "" + msgid "IssueAnalytics|Age" msgstr "" @@ -20717,7 +20720,7 @@ msgstr "" msgid "No estimate or time spent" msgstr "" -msgid "No file chosen" +msgid "No file chosen." msgstr "" msgid "No file hooks found." @@ -20768,6 +20771,9 @@ msgstr "" msgid "No matches found" msgstr "" +msgid "No matching issue found. Make sure that you are adding a valid issue URL." +msgstr "" + msgid "No matching labels" msgstr "" @@ -23380,7 +23386,7 @@ msgstr "" msgid "Profiles|Main settings" msgstr "" -msgid "Profiles|No file chosen" +msgid "Profiles|No file chosen." msgstr "" msgid "Profiles|Notification email" @@ -27277,9 +27283,6 @@ msgstr "" msgid "Select projects" msgstr "" -msgid "Select required regulatory standard." -msgstr "" - msgid "Select reviewer(s)" msgstr "" diff --git a/spec/controllers/projects/registry/repositories_controller_spec.rb b/spec/controllers/projects/registry/repositories_controller_spec.rb index 9b803edd463..0685e5a2055 100644 --- a/spec/controllers/projects/registry/repositories_controller_spec.rb +++ b/spec/controllers/projects/registry/repositories_controller_spec.rb @@ -16,19 +16,19 @@ RSpec.describe Projects::Registry::RepositoriesController do project.add_developer(user) end - shared_examples 'with name parameter' do - let_it_be(:repo) { create(:container_repository, project: project, name: 'my_searched_image') } - let_it_be(:another_repo) { create(:container_repository, project: project, name: 'bar') } - - it 'returns the searched repo' do - go_to_index(format: :json, params: { name: 'my_searched_image' }) + shared_examples 'renders 200 for html and 404 for json' do + it 'successfully renders container repositories', :snowplow do + go_to_index expect(response).to have_gitlab_http_status(:ok) - expect(json_response.length).to eq 1 - expect(json_response.first).to include( - 'id' => repo.id, - 'name' => repo.name - ) + # event tracked in GraphQL API: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44926 + expect_no_snowplow_event + end + + it 'returns 404 for request in json format' do + go_to_index(format: :json) + + expect(response).to have_gitlab_http_status(:not_found) end end @@ -50,33 +50,12 @@ RSpec.describe Projects::Registry::RepositoriesController do tags: %w[rc1 latest]) end - it 'successfully renders container repositories', :snowplow do - go_to_index - - expect_no_snowplow_event - expect(response).to have_gitlab_http_status(:ok) - end - - it 'tracks the event', :snowplow do - go_to_index(format: :json) - - expect_snowplow_event(category: anything, action: 'list_repositories') - end - it 'creates a root container repository' do expect { go_to_index }.to change { ContainerRepository.all.count }.by(1) expect(ContainerRepository.first).to be_root_repository end - it 'json has a list of projects' do - go_to_index(format: :json) - - expect(response).to have_gitlab_http_status(:ok) - expect(response).to match_response_schema('registry/repositories') - expect(response).to include_pagination_headers - end - - it_behaves_like 'with name parameter' + it_behaves_like 'renders 200 for html and 404 for json' end context 'when there are no tags for this repository' do @@ -84,22 +63,11 @@ RSpec.describe Projects::Registry::RepositoriesController do stub_container_registry_tags(repository: :any, tags: []) end - it 'successfully renders container repositories' do - go_to_index - - expect(response).to have_gitlab_http_status(:ok) - end - it 'does not ensure root container repository' do expect { go_to_index }.not_to change { ContainerRepository.all.count } end - it 'responds with json if asked' do - go_to_index(format: :json) - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_kind_of(Array) - end + it_behaves_like 'renders 200 for html and 404 for json' end end end diff --git a/spec/frontend/environments/enable_review_app_modal_spec.js b/spec/frontend/environments/enable_review_app_modal_spec.js index f5063cff620..9a3f13f19d5 100644 --- a/spec/frontend/environments/enable_review_app_modal_spec.js +++ b/spec/frontend/environments/enable_review_app_modal_spec.js @@ -1,4 +1,5 @@ import { shallowMount } from '@vue/test-utils'; +import { extendedWrapper } from 'helpers/vue_test_utils_helper'; import EnableReviewAppButton from '~/environments/components/enable_review_app_modal.vue'; import ModalCopyButton from '~/vue_shared/components/modal_copy_button.vue'; @@ -11,15 +12,25 @@ describe('Enable Review App Button', () => { describe('renders the modal', () => { beforeEach(() => { - wrapper = shallowMount(EnableReviewAppButton, { - propsData: { - modalId: 'fake-id', - }, - }); + wrapper = extendedWrapper( + shallowMount(EnableReviewAppButton, { + propsData: { + modalId: 'fake-id', + }, + provide: { + defaultBranchName: 'main', + }, + }), + ); + }); + + it('renders the defaultBranchName copy', () => { + const findCopyString = () => wrapper.findByTestId('enable-review-app-copy-string'); + expect(findCopyString().text()).toContain('- main'); }); it('renders the copyToClipboard button', () => { - expect(wrapper.find(ModalCopyButton).exists()).toBe(true); + expect(wrapper.findComponent(ModalCopyButton).exists()).toBe(true); }); }); }); diff --git a/spec/frontend/mr_notes/stores/actions_spec.js b/spec/frontend/mr_notes/stores/actions_spec.js new file mode 100644 index 00000000000..dbceedface1 --- /dev/null +++ b/spec/frontend/mr_notes/stores/actions_spec.js @@ -0,0 +1,25 @@ +import testAction from 'helpers/vuex_action_helper'; +import { setEndpoints } from '~/mr_notes/stores/actions'; +import mutationTypes from '~/mr_notes/stores/mutation_types'; + +describe('MR Notes Mutator Actions', () => { + describe('setEndpoints', () => { + it('should trigger the SET_ENDPOINTS state mutation', (done) => { + const endpoints = { endpointA: 'a' }; + + testAction( + setEndpoints, + endpoints, + {}, + [ + { + type: mutationTypes.SET_ENDPOINTS, + payload: endpoints, + }, + ], + [], + done, + ); + }); + }); +}); diff --git a/spec/frontend/mr_notes/stores/mutations_spec.js b/spec/frontend/mr_notes/stores/mutations_spec.js new file mode 100644 index 00000000000..422db3d5a38 --- /dev/null +++ b/spec/frontend/mr_notes/stores/mutations_spec.js @@ -0,0 +1,15 @@ +import mutationTypes from '~/mr_notes/stores/mutation_types'; +import mutations from '~/mr_notes/stores/mutations'; + +describe('MR Notes Mutations', () => { + describe(mutationTypes.SET_ENDPOINTS, () => { + it('should set the endpoints value', () => { + const state = {}; + const endpoints = { endpointA: 'A', endpointB: 'B' }; + + mutations[mutationTypes.SET_ENDPOINTS](state, endpoints); + + expect(state.endpoints).toEqual(endpoints); + }); + }); +}); diff --git a/spec/frontend/repository/components/__snapshots__/directory_download_links_spec.js.snap b/spec/frontend/repository/components/__snapshots__/directory_download_links_spec.js.snap index 6968fb3e153..836ae5c22e6 100644 --- a/spec/frontend/repository/components/__snapshots__/directory_download_links_spec.js.snap +++ b/spec/frontend/repository/components/__snapshots__/directory_download_links_spec.js.snap @@ -16,22 +16,30 @@ exports[`Repository directory download links component renders downloads links f <div class="btn-group ml-0 w-100" > - <gl-link-stub - class="btn btn-xs btn-primary" + <gl-button-stub + buttontextclasses="" + category="primary" href="http://test.com/?path=app" + icon="" + size="small" + variant="confirm" > zip - </gl-link-stub> - <gl-link-stub - class="btn btn-xs" + </gl-button-stub> + <gl-button-stub + buttontextclasses="" + category="primary" href="http://test.com/?path=app" + icon="" + size="small" + variant="default" > tar - </gl-link-stub> + </gl-button-stub> </div> </div> </section> @@ -53,22 +61,30 @@ exports[`Repository directory download links component renders downloads links f <div class="btn-group ml-0 w-100" > - <gl-link-stub - class="btn btn-xs btn-primary" + <gl-button-stub + buttontextclasses="" + category="primary" href="http://test.com/?path=app/assets" + icon="" + size="small" + variant="confirm" > zip - </gl-link-stub> - <gl-link-stub - class="btn btn-xs" + </gl-button-stub> + <gl-button-stub + buttontextclasses="" + category="primary" href="http://test.com/?path=app/assets" + icon="" + size="small" + variant="default" > tar - </gl-link-stub> + </gl-button-stub> </div> </div> </section> diff --git a/spec/graphql/resolvers/group_milestones_resolver_spec.rb b/spec/graphql/resolvers/group_milestones_resolver_spec.rb index d8ff8e9c1f2..dd3f1676538 100644 --- a/spec/graphql/resolvers/group_milestones_resolver_spec.rb +++ b/spec/graphql/resolvers/group_milestones_resolver_spec.rb @@ -136,5 +136,56 @@ RSpec.describe Resolvers::GroupMilestonesResolver do expect(resolve_group_milestones(args)).to match_array([milestone1, milestone2, milestone3]) end end + + describe 'include_descendants and include_ancestors' do + let_it_be(:parent_group) { create(:group, :public) } + let_it_be(:group) { create(:group, :public, parent: parent_group) } + let_it_be(:accessible_group) { create(:group, :private, parent: group) } + let_it_be(:accessible_project) { create(:project, group: accessible_group) } + let_it_be(:inaccessible_group) { create(:group, :private, parent: group) } + let_it_be(:inaccessible_project) { create(:project, :private, group: group) } + let_it_be(:milestone1) { create(:milestone, group: group) } + let_it_be(:milestone2) { create(:milestone, group: accessible_group) } + let_it_be(:milestone3) { create(:milestone, project: accessible_project) } + let_it_be(:milestone4) { create(:milestone, group: inaccessible_group) } + let_it_be(:milestone5) { create(:milestone, project: inaccessible_project) } + let_it_be(:milestone6) { create(:milestone, group: parent_group) } + + before do + accessible_group.add_developer(current_user) + end + + context 'when including neither ancestor or descendant milestones in a public group' do + let(:args) { {} } + + it 'finds milestones only in accessible projects and groups' do + expect(resolve_group_milestones(args)).to match_array([milestone1]) + end + end + + context 'when including descendant milestones in a public group' do + let(:args) { { include_descendants: true } } + + it 'finds milestones only in accessible projects and groups' do + expect(resolve_group_milestones(args)).to match_array([milestone1, milestone2, milestone3]) + end + end + + context 'when including ancestor milestones in a public group' do + let(:args) { { include_ancestors: true } } + + it 'finds milestones only in accessible projects and groups' do + expect(resolve_group_milestones(args)).to match_array([milestone1, milestone6]) + end + end + + context 'when including both ancestor or descendant milestones in a public group' do + let(:args) { { include_descendants: true, include_ancestors: true } } + + it 'finds milestones only in accessible projects and groups' do + expect(resolve_group_milestones(args)).to match_array([milestone1, milestone2, milestone3, milestone6]) + end + end + end end end diff --git a/spec/graphql/types/ci/job_type_spec.rb b/spec/graphql/types/ci/job_type_spec.rb index 25f626cea0f..c54137a1c3e 100644 --- a/spec/graphql/types/ci/job_type_spec.rb +++ b/spec/graphql/types/ci/job_type_spec.rb @@ -8,6 +8,8 @@ RSpec.describe Types::Ci::JobType do it 'exposes the expected fields' do expected_fields = %i[ + id + shortSha pipeline name needs diff --git a/spec/initializers/kramdown_patch_spec.rb b/spec/initializers/kramdown_patch_spec.rb new file mode 100644 index 00000000000..49dda9252bb --- /dev/null +++ b/spec/initializers/kramdown_patch_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'Kramdown patch for syntax highlighting formatters' do + subject { Kramdown::Document.new(options + "\n" + code).to_html } + + let(:code) do + <<-RUBY +~~~ ruby + def what? + 42 + end +~~~ + RUBY + end + + context 'with invalid formatter' do + let(:options) { %({::options auto_ids="false" footnote_nr="5" syntax_highlighter="rouge" syntax_highlighter_opts="{formatter: CSV, line_numbers: true\\}" /}) } + + it 'falls back to standard HTML and disallows CSV' do + expect(CSV).not_to receive(:new) + expect(::Rouge::Formatters::HTML).to receive(:new).and_call_original + + expect(subject).to be_present + end + end + + context 'with valid formatter' do + let(:options) { %({::options auto_ids="false" footnote_nr="5" syntax_highlighter="rouge" syntax_highlighter_opts="{formatter: HTMLLegacy\\}" /}) } + + it 'allows formatter' do + expect(::Rouge::Formatters::HTMLLegacy).to receive(:new).and_call_original + + expect(subject).to be_present + end + end +end diff --git a/spec/models/experiment_spec.rb b/spec/models/experiment_spec.rb index 09dd1766acc..1517f426fa3 100644 --- a/spec/models/experiment_spec.rb +++ b/spec/models/experiment_spec.rb @@ -244,18 +244,27 @@ RSpec.describe Experiment do context 'when no existing experiment_subject record exists for the given group' do it 'creates an experiment_subject record' do - expect_next(ExperimentSubject).to receive(:update!).with(variant: variant).and_call_original - expect { record_group_and_variant! }.to change(ExperimentSubject, :count).by(1) + expect(ExperimentSubject.last.variant).to eq(variant.to_s) end end context 'when an existing experiment_subject exists for the given group' do - context 'but it belonged to a different variant' do - let!(:experiment_subject) do - create(:experiment_subject, experiment: experiment, group: group, user: nil, variant: :experimental) + let_it_be(:experiment_subject) do + create(:experiment_subject, experiment: experiment, group: group, user: nil, variant: :experimental) + end + + context 'when it belongs to the same variant' do + let(:variant) { :experimental } + + it 'does not initiate a transaction' do + expect(ActiveRecord::Base.connection).not_to receive(:transaction) + + subject end + end + context 'but it belonged to a different variant' do it 'updates the variant value' do expect { record_group_and_variant! }.to change { experiment_subject.reload.variant }.to('control') end @@ -299,6 +308,16 @@ RSpec.describe Experiment do expect { subject }.not_to change(ExperimentUser, :count) end + context 'when group type or context did not change' do + let(:context) { {} } + + it 'does not initiate a transaction' do + expect(ActiveRecord::Base.connection).not_to receive(:transaction) + + subject + end + end + context 'but the group_type and context has changed' do let(:group) { :experimental } diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 8c7289adbcc..b2c45135de1 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1353,6 +1353,24 @@ RSpec.describe MergeRequest, factory_default: :keep do expect(subject.work_in_progress?).to eq false end + it 'does not detect Draft: in the middle of the title' do + subject.title = 'Something with Draft: in the middle' + + expect(subject.work_in_progress?).to eq false + end + + it 'does not detect WIP at the end of the title' do + subject.title = 'Something ends with WIP' + + expect(subject.work_in_progress?).to eq false + end + + it 'does not detect Draft at the end of the title' do + subject.title = 'Something ends with Draft' + + expect(subject.work_in_progress?).to eq false + end + it "doesn't detect WIP for words starting with WIP" do subject.title = "Wipwap #{subject.title}" expect(subject.work_in_progress?).to eq false @@ -1363,6 +1381,11 @@ RSpec.describe MergeRequest, factory_default: :keep do expect(subject.work_in_progress?).to eq false end + it "doesn't detect draft for words containing with draft" do + subject.title = "Drafting #{subject.title}" + expect(subject.work_in_progress?).to eq false + end + it "doesn't detect WIP by default" do expect(subject.work_in_progress?).to eq false end @@ -1393,6 +1416,42 @@ RSpec.describe MergeRequest, factory_default: :keep do expect(subject.work_in_progress?).to eq false end end + + it 'removes only WIP prefix from the MR title' do + subject.title = 'WIP: Implement feature called WIP' + + expect(subject.wipless_title).to eq 'Implement feature called WIP' + end + + it 'removes only draft prefix from the MR title' do + subject.title = 'Draft: Implement feature called draft' + + expect(subject.wipless_title).to eq 'Implement feature called draft' + end + + it 'does not remove WIP in the middle of the title' do + subject.title = 'Something with WIP in the middle' + + expect(subject.wipless_title).to eq subject.title + end + + it 'does not remove Draft in the middle of the title' do + subject.title = 'Something with Draft in the middle' + + expect(subject.wipless_title).to eq subject.title + end + + it 'does not remove WIP at the end of the title' do + subject.title = 'Something ends with WIP' + + expect(subject.wipless_title).to eq subject.title + end + + it 'does not remove Draft at the end of the title' do + subject.title = 'Something ends with Draft' + + expect(subject.wipless_title).to eq subject.title + end end describe "#wip_title" do diff --git a/spec/requests/api/graphql/group/milestones_spec.rb b/spec/requests/api/graphql/group/milestones_spec.rb index 380eaea17f8..a5b489d72fd 100644 --- a/spec/requests/api/graphql/group/milestones_spec.rb +++ b/spec/requests/api/graphql/group/milestones_spec.rb @@ -9,12 +9,14 @@ RSpec.describe 'Milestones through GroupQuery' do let_it_be(:now) { Time.now } describe 'Get list of milestones from a group' do - let_it_be(:group) { create(:group) } + let_it_be(:parent_group) { create(:group) } + let_it_be(:group) { create(:group, parent: parent_group) } let_it_be(:milestone_1) { create(:milestone, group: group) } let_it_be(:milestone_2) { create(:milestone, group: group, state: :closed, start_date: now, due_date: now + 1.day) } let_it_be(:milestone_3) { create(:milestone, group: group, start_date: now, due_date: now + 2.days) } let_it_be(:milestone_4) { create(:milestone, group: group, state: :closed, start_date: now - 2.days, due_date: now - 1.day) } let_it_be(:milestone_from_other_group) { create(:milestone, group: create(:group)) } + let_it_be(:parent_milestone) { create(:milestone, group: parent_group) } let(:milestone_data) { graphql_data['group']['milestones']['edges'] } @@ -64,14 +66,32 @@ RSpec.describe 'Milestones through GroupQuery' do accessible_group.add_developer(user) end - it 'returns milestones also from subgroups and subprojects visible to user' do - fetch_milestones(user, args) + context 'when including decendants' do + let(:args) { { include_descendants: true } } + + it 'returns milestones also from subgroups and subprojects visible to user' do + fetch_milestones(user, args) + + expect_array_response( + milestone_1.to_global_id.to_s, milestone_2.to_global_id.to_s, + milestone_3.to_global_id.to_s, milestone_4.to_global_id.to_s, + submilestone_1.to_global_id.to_s, submilestone_2.to_global_id.to_s + ) + end + end + + context 'when including ancestors' do + let(:args) { { include_ancestors: true } } - expect_array_response( - milestone_1.to_global_id.to_s, milestone_2.to_global_id.to_s, - milestone_3.to_global_id.to_s, milestone_4.to_global_id.to_s, - submilestone_1.to_global_id.to_s, submilestone_2.to_global_id.to_s - ) + it 'returns milestones from ancestor groups' do + fetch_milestones(user, args) + + expect_array_response( + milestone_1.to_global_id.to_s, milestone_2.to_global_id.to_s, + milestone_3.to_global_id.to_s, milestone_4.to_global_id.to_s, + parent_milestone.to_global_id.to_s + ) + end end end diff --git a/spec/requests/api/issue_links_spec.rb b/spec/requests/api/issue_links_spec.rb index a4243766111..b64d395f25f 100644 --- a/spec/requests/api/issue_links_spec.rb +++ b/spec/requests/api/issue_links_spec.rb @@ -82,7 +82,7 @@ RSpec.describe API::IssueLinks do params: { target_project_id: unauthorized_project.id, target_issue_iid: target_issue.iid } expect(response).to have_gitlab_http_status(:not_found) - expect(json_response['message']).to eq('No Issue found for given params') + expect(json_response['message']).to eq('No matching issue found. Make sure that you are adding a valid issue URL.') end end diff --git a/spec/requests/projects/issue_links_controller_spec.rb b/spec/requests/projects/issue_links_controller_spec.rb index a21c676f000..d22955718f8 100644 --- a/spec/requests/projects/issue_links_controller_spec.rb +++ b/spec/requests/projects/issue_links_controller_spec.rb @@ -71,7 +71,7 @@ RSpec.describe Projects::IssueLinksController do list_service_response = IssueLinks::ListService.new(issue, user).execute expect(response).to have_gitlab_http_status(:not_found) - expect(json_response).to eq('message' => 'No Issue found for given params', 'issuables' => list_service_response.as_json) + expect(json_response).to eq('message' => 'No matching issue found. Make sure that you are adding a valid issue URL.', 'issuables' => list_service_response.as_json) end end end diff --git a/spec/services/issue_links/create_service_spec.rb b/spec/services/issue_links/create_service_spec.rb index 873890d25cf..1bca717acb7 100644 --- a/spec/services/issue_links/create_service_spec.rb +++ b/spec/services/issue_links/create_service_spec.rb @@ -24,7 +24,7 @@ RSpec.describe IssueLinks::CreateService do end it 'returns error' do - is_expected.to eq(message: 'No Issue found for given params', status: :error, http_status: 404) + is_expected.to eq(message: 'No matching issue found. Make sure that you are adding a valid issue URL.', status: :error, http_status: 404) end end @@ -34,7 +34,7 @@ RSpec.describe IssueLinks::CreateService do end it 'returns error' do - is_expected.to eq(message: 'No Issue found for given params', status: :error, http_status: 404) + is_expected.to eq(message: 'No matching issue found. Make sure that you are adding a valid issue URL.', status: :error, http_status: 404) end it 'no relationship is created' do @@ -52,7 +52,7 @@ RSpec.describe IssueLinks::CreateService do it 'returns error' do target_issuable.project.add_guest(user) - is_expected.to eq(message: 'No Issue found for given params', status: :error, http_status: 404) + is_expected.to eq(message: 'No matching issue found. Make sure that you are adding a valid issue URL.', status: :error, http_status: 404) end it 'no relationship is created' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 5f1513b1247..5ffc9d778d1 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -104,9 +104,9 @@ RSpec.configure do |config| warn "=== uptime" warn `uptime` warn "=== Prometheus metrics:" - warn `curl -s http://localhost:9236/metrics` + warn `curl -s -o log/gitaly-metrics.log http://localhost:9236/metrics` warn "=== Taking goroutine dump in log/goroutines.log..." - warn `curl -o log/goroutines.log http://localhost:9236/debug/pprof/goroutine?debug=2` + warn `curl -s -o log/goroutines.log http://localhost:9236/debug/pprof/goroutine?debug=2` end end end |