diff options
32 files changed, 652 insertions, 72 deletions
diff --git a/.gitignore b/.gitignore index 5cdab272055..b3e6cbae96b 100644 --- a/.gitignore +++ b/.gitignore @@ -88,3 +88,4 @@ jsdoc/ /qa/.rakeTasks webpack-dev-server.json /.nvimrc +.solargraph.yml diff --git a/GITLAB_PAGES_VERSION b/GITLAB_PAGES_VERSION index 850e742404b..141f2e805be 100644 --- a/GITLAB_PAGES_VERSION +++ b/GITLAB_PAGES_VERSION @@ -1 +1 @@ -1.14.0 +1.15.0 diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 0cdda48f1e5..5c643a864d6 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -532,44 +532,41 @@ export default { :show-panels="showPanels" :collapse-group="collapseGroup(groupData.key)" > - <div v-if="!groupSingleEmptyState(groupData.key)"> - <vue-draggable - :value="groupData.panels" - group="metrics-dashboard" - :component-data="{ attrs: { class: 'row mx-0 w-100' } }" - :disabled="!isRearrangingPanels" - @input="updatePanels(groupData.key, $event)" + <vue-draggable + v-if="!groupSingleEmptyState(groupData.key)" + :value="groupData.panels" + group="metrics-dashboard" + :component-data="{ attrs: { class: 'row mx-0 w-100' } }" + :disabled="!isRearrangingPanels" + @input="updatePanels(groupData.key, $event)" + > + <div + v-for="(graphData, graphIndex) in groupData.panels" + :key="`panel-type-${graphIndex}`" + class="col-12 col-lg-6 px-2 mb-2 draggable" + :class="{ 'draggable-enabled': isRearrangingPanels }" > - <div - v-for="(graphData, graphIndex) in groupData.panels" - :key="`panel-type-${graphIndex}`" - class="col-12 col-lg-6 px-2 mb-2 draggable" - :class="{ 'draggable-enabled': isRearrangingPanels }" - > - <div class="position-relative draggable-panel js-draggable-panel"> - <div - v-if="isRearrangingPanels" - class="draggable-remove js-draggable-remove p-2 w-100 position-absolute d-flex justify-content-end" - @click="removePanel(groupData.key, groupData.panels, graphIndex)" - > - <a class="mx-2 p-2 draggable-remove-link" :aria-label="__('Remove')"> - <icon name="close" /> - </a> - </div> - - <panel-type - :clipboard-text=" - generateLink(groupData.group, graphData.title, graphData.y_label) - " - :graph-data="graphData" - :alerts-endpoint="alertsEndpoint" - :prometheus-alerts-available="prometheusAlertsAvailable" - :index="`${index}-${graphIndex}`" - /> + <div class="position-relative draggable-panel js-draggable-panel"> + <div + v-if="isRearrangingPanels" + class="draggable-remove js-draggable-remove p-2 w-100 position-absolute d-flex justify-content-end" + @click="removePanel(groupData.key, groupData.panels, graphIndex)" + > + <a class="mx-2 p-2 draggable-remove-link" :aria-label="__('Remove')"> + <icon name="close" /> + </a> </div> + + <panel-type + :clipboard-text="generateLink(groupData.group, graphData.title, graphData.y_label)" + :graph-data="graphData" + :alerts-endpoint="alertsEndpoint" + :prometheus-alerts-available="prometheusAlertsAvailable" + :index="`${index}-${graphIndex}`" + /> </div> - </vue-draggable> - </div> + </div> + </vue-draggable> <div v-else class="py-5 col col-sm-10 col-md-8 col-lg-7 col-xl-6"> <group-empty-state ref="empty-group" diff --git a/app/assets/javascripts/pages/projects/wikis/wikis.js b/app/assets/javascripts/pages/projects/wikis/wikis.js index 80b62859134..6b02a074abf 100644 --- a/app/assets/javascripts/pages/projects/wikis/wikis.js +++ b/app/assets/javascripts/pages/projects/wikis/wikis.js @@ -40,7 +40,7 @@ export default class Wikis { // Replace hyphens with spaces if (title) title = title.replace(/-+/g, ' '); - const newCommitMessage = sprintf(this.commitMessageI18n, { pageTitle: title }); + const newCommitMessage = sprintf(this.commitMessageI18n, { pageTitle: title }, false); this.commitMessageInput.value = newCommitMessage; } diff --git a/app/views/admin/application_settings/_account_and_limit.html.haml b/app/views/admin/application_settings/_account_and_limit.html.haml index 4ab0ec90735..aa377886edc 100644 --- a/app/views/admin/application_settings/_account_and_limit.html.haml +++ b/app/views/admin/application_settings/_account_and_limit.html.haml @@ -8,20 +8,20 @@ = f.label :gravatar_enabled, class: 'form-check-label' do = _('Gravatar enabled') .form-group - = f.label :default_projects_limit, class: 'label-bold' - = f.number_field :default_projects_limit, class: 'form-control' + = f.label :default_projects_limit, _('Default projects limit'), class: 'label-bold' + = f.number_field :default_projects_limit, class: 'form-control', title: _('Maximum number of projects.'), data: { toggle: 'tooltip', container: 'body' } .form-group = f.label :max_attachment_size, _('Maximum attachment size (MB)'), class: 'label-bold' - = f.number_field :max_attachment_size, class: 'form-control' + = f.number_field :max_attachment_size, class: 'form-control', title: _('Maximum size of individual attachments in comments.'), data: { toggle: 'tooltip', container: 'body' } = render_if_exists 'admin/application_settings/repository_size_limit_setting', form: f .form-group = f.label :receive_max_input_size, _('Maximum push size (MB)'), class: 'label-light' - = f.number_field :receive_max_input_size, class: 'form-control qa-receive-max-input-size-field' + = f.number_field :receive_max_input_size, class: 'form-control qa-receive-max-input-size-field', title: _('Maximum size limit for a single commit.'), data: { toggle: 'tooltip', container: 'body' } .form-group = f.label :session_expire_delay, _('Session duration (minutes)'), class: 'label-light' - = f.number_field :session_expire_delay, class: 'form-control' + = f.number_field :session_expire_delay, class: 'form-control', title: _('Maximum duration of a session.'), data: { toggle: 'tooltip', container: 'body' } %span.form-text.text-muted#session_expire_delay_help_block= _('GitLab restart is required to apply changes.') = render_if_exists 'admin/application_settings/personal_access_token_expiration_policy', form: f diff --git a/app/views/admin/application_settings/general.html.haml b/app/views/admin/application_settings/general.html.haml index b9f49fdc9de..94048060767 100644 --- a/app/views/admin/application_settings/general.html.haml +++ b/app/views/admin/application_settings/general.html.haml @@ -20,7 +20,7 @@ %button.btn.js-settings-toggle{ type: 'button' } = expanded_by_default? ? _('Collapse') : _('Expand') %p - = _('Session expiration, projects limit and attachment size.') + = _('Set projects and maximum size limits, session duration, user options, and check feature availability for namespace plan.') .settings-content = render 'account_and_limit' diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml index 33b56655206..9577a2a79df 100644 --- a/app/views/admin/broadcast_messages/_form.html.haml +++ b/app/views/admin/broadcast_messages/_form.html.haml @@ -5,15 +5,14 @@ = render_broadcast_message(@broadcast_message) - else Your message here -- if Feature.enabled?(:broadcast_notification_type) - .d-flex.justify-content-center - .broadcast-notification-message.preview.js-broadcast-notification-message-preview.mt-2{ class: ('hidden' unless @broadcast_message.notification? ) } - = sprite_icon('bullhorn', size: 16, css_class:'vertical-align-text-top') - .js-broadcast-message-preview - - if @broadcast_message.message.present? - = render_broadcast_message(@broadcast_message) - - else - Your message here +.d-flex.justify-content-center + .broadcast-notification-message.preview.js-broadcast-notification-message-preview.mt-2{ class: ('hidden' unless @broadcast_message.notification? ) } + = sprite_icon('bullhorn', size: 16, css_class:'vertical-align-text-top') + .js-broadcast-message-preview + - if @broadcast_message.message.present? + = render_broadcast_message(@broadcast_message) + - else + Your message here = form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form js-quick-submit js-requires-input'} do |f| = form_errors(@broadcast_message) @@ -26,12 +25,11 @@ required: true, dir: 'auto', data: { preview_path: preview_admin_broadcast_messages_path } - - if Feature.enabled?(:broadcast_notification_type) - .form-group.row - .col-sm-2.col-form-label - = f.label :broadcast_type, _('Type') - .col-sm-10 - = f.select :broadcast_type, broadcast_type_options, {}, class: 'form-control js-broadcast-message-type' + .form-group.row + .col-sm-2.col-form-label + = f.label :broadcast_type, _('Type') + .col-sm-10 + = f.select :broadcast_type, broadcast_type_options, {}, class: 'form-control js-broadcast-message-type' .form-group.row.js-broadcast-message-background-color-form-group{ class: ('hidden' unless @broadcast_message.banner? ) } .col-sm-2.col-form-label = f.label :color, _("Background color") diff --git a/app/views/import/shared/_new_project_form.html.haml b/app/views/import/shared/_new_project_form.html.haml index 4d13d4f2869..35059229a55 100644 --- a/app/views/import/shared/_new_project_form.html.haml +++ b/app/views/import/shared/_new_project_form.html.haml @@ -10,7 +10,7 @@ .input-group-prepend.flex-shrink-0.has-tooltip{ title: root_url } .input-group-text = root_url - = select_tag :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), class: 'select2 js-select-namespace', tabindex: 1 + = select_tag :namespace_id, namespaces_options(namespace_id_from(params) || :current_user, display_path: true, extra_group: namespace_id_from(params)), class: 'select2 js-select-namespace block-truncated', tabindex: 1 - else .input-group-prepend.static-namespace.has-tooltip{ title: user_url(current_user.username) + '/' } .input-group-text.border-0 diff --git a/app/views/layouts/_broadcast.html.haml b/app/views/layouts/_broadcast.html.haml index 9d7ad249ac8..4c4fc6411b8 100644 --- a/app/views/layouts/_broadcast.html.haml +++ b/app/views/layouts/_broadcast.html.haml @@ -1,4 +1,3 @@ - current_broadcast_banner_messages.each do |message| = broadcast_message(message) -- if Feature.enabled?(:broadcast_notification_type) - = broadcast_message(current_broadcast_notification_message) += broadcast_message(current_broadcast_notification_message) diff --git a/changelogs/unreleased/197932-add-package-tags-display-to-package-list-page.yml b/changelogs/unreleased/197932-add-package-tags-display-to-package-list-page.yml new file mode 100644 index 00000000000..d0db5339c2b --- /dev/null +++ b/changelogs/unreleased/197932-add-package-tags-display-to-package-list-page.yml @@ -0,0 +1,5 @@ +--- +title: Displays package tags next to the name on the new package list page +merge_request: 23675 +author: +type: added diff --git a/changelogs/unreleased/198464-add-can_create_merge_request_in-to-project-api-response.yml b/changelogs/unreleased/198464-add-can_create_merge_request_in-to-project-api-response.yml new file mode 100644 index 00000000000..9cc9643d0c8 --- /dev/null +++ b/changelogs/unreleased/198464-add-can_create_merge_request_in-to-project-api-response.yml @@ -0,0 +1,5 @@ +--- +title: Add can_create_merge_request_in to /project/:id API response +merge_request: 23577 +author: +type: added diff --git a/changelogs/unreleased/21371-auto-generated-wiki-commit-message-having-html-encoded-entities.yml b/changelogs/unreleased/21371-auto-generated-wiki-commit-message-having-html-encoded-entities.yml new file mode 100644 index 00000000000..a588b60864a --- /dev/null +++ b/changelogs/unreleased/21371-auto-generated-wiki-commit-message-having-html-encoded-entities.yml @@ -0,0 +1,5 @@ +--- +title: Auto generated wiki commit message containing HTML encoded entities +merge_request: 21371 +author: 2knal +type: other diff --git a/changelogs/unreleased/28872-admin-area-s-account-and-limit-fields-could-be-improved.yml b/changelogs/unreleased/28872-admin-area-s-account-and-limit-fields-could-be-improved.yml new file mode 100644 index 00000000000..64142fd7f88 --- /dev/null +++ b/changelogs/unreleased/28872-admin-area-s-account-and-limit-fields-could-be-improved.yml @@ -0,0 +1,5 @@ +--- +title: Add clarifying content to account fields +merge_request: +author: +type: other diff --git a/changelogs/unreleased/jivanvl-fix-charts-shrinkage.yml b/changelogs/unreleased/jivanvl-fix-charts-shrinkage.yml new file mode 100644 index 00000000000..9cc1a91c442 --- /dev/null +++ b/changelogs/unreleased/jivanvl-fix-charts-shrinkage.yml @@ -0,0 +1,5 @@ +--- +title: Fix custom charts in monitoring dashboard shrinking +merge_request: 23649 +author: +type: fixed diff --git a/changelogs/unreleased/mr-approvals-anchors.yml b/changelogs/unreleased/mr-approvals-anchors.yml new file mode 100644 index 00000000000..72f6d7b5bc3 --- /dev/null +++ b/changelogs/unreleased/mr-approvals-anchors.yml @@ -0,0 +1,5 @@ +--- +title: Update links related to MR approvals in UI +merge_request: 23948 +author: +type: other diff --git a/changelogs/unreleased/nicolasdular-add-broadcast-types.yml b/changelogs/unreleased/nicolasdular-add-broadcast-types.yml new file mode 100644 index 00000000000..44345f74e14 --- /dev/null +++ b/changelogs/unreleased/nicolasdular-add-broadcast-types.yml @@ -0,0 +1,5 @@ +--- +title: Add broadcast types to broadcast messages +merge_request: +author: +type: added diff --git a/changelogs/unreleased/pages-version-v1-15-0.yml b/changelogs/unreleased/pages-version-v1-15-0.yml new file mode 100644 index 00000000000..13b5764d612 --- /dev/null +++ b/changelogs/unreleased/pages-version-v1-15-0.yml @@ -0,0 +1,5 @@ +--- +title: Upgrade Pages to 1.15.0 +merge_request: 24043 +author: +type: added diff --git a/changelogs/unreleased/prevent-project-path-namespace-overflow-during-import.yml b/changelogs/unreleased/prevent-project-path-namespace-overflow-during-import.yml new file mode 100644 index 00000000000..e48d44d0f5d --- /dev/null +++ b/changelogs/unreleased/prevent-project-path-namespace-overflow-during-import.yml @@ -0,0 +1,5 @@ +--- +title: Prevent project path namespace overflow during import +merge_request: 24042 +author: George Tsiolis +type: fixed diff --git a/doc/.linting/vale/styles/gitlab/Contractions.yml b/doc/.linting/vale/styles/gitlab/Contractions.yml new file mode 100644 index 00000000000..0f31f6b6aa9 --- /dev/null +++ b/doc/.linting/vale/styles/gitlab/Contractions.yml @@ -0,0 +1,76 @@ +--- +# `extends` indicates the Vale extension point being used. +# Full list of styles: https://errata-ai.github.io/vale/styles/ +extends: substitution + +# Substitution rules can display the matched and suggested strings in the +# message shown to the user. The first use of %s prints the suggested option, +# and the second use of %s displays what was found in the text. +message: Use "%s" instead of "%s" in most cases. + +# Should a result be flagged as a suggestion, warning, or error? +# Results that fall below the MinAlertLevel set in +# https://gitlab.com/gitlab-org/gitlab/blob/master/.vale.ini won't be shown. +level: suggestion + +# Should a match be case-insensitive or case-sensitive? +# Acceptable values are 'true' or 'false' +ignorecase: true + +# Should this rule be limited to a specific scope? If yes, uncomment the line. +# Possible scopes: https://errata-ai.github.io/vale/formats/#available-scopes +# scope: heading + +# Should this rule ignore normal word boundaries, such as \b ? +# Acceptable values are 'true' or 'false' +nonword: false + +# What is the source for this rule? +link: https://docs.gitlab.com/ee/development/documentation/styleguide.html#language + +# The 'swap' section provides a list of values, one per line, in the form of +# $bad: $good +swap: + + # Common contractions are ok + it is: it's + can not: can't + cannot: can't + do not: don't + have not: haven't + that is: that's + we are: we're + will not: won't + would not: wouldn't + you are: you're + you have: you've + + # Uncommon contractions are not ok + aren't: are not + couldn't: could not + didn't: did not + doesn't: does not + hasn't: has not + how'll: how will + how's: how is + isn't: is not + it'll: it will + shouldn't: should not + that'll: that will + they'll: they will + they're: they are + wasn't: was not + weren't: were not + we'll: we will + we've: we have + what's: what is + what'll: what will + when's: when is + when'll: when will + where's: where is + where'll: where will + who's: who is + who'll: who will + why's: why is + why'll: why will + diff --git a/doc/api/projects.md b/doc/api/projects.md index d67d8949571..ad1cc4ea1b7 100644 --- a/doc/api/projects.md +++ b/doc/api/projects.md @@ -129,6 +129,7 @@ When the user is authenticated and `simple` is not set this returns something li "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "created_at": "2013-09-30T13:46:02Z", @@ -205,6 +206,7 @@ When the user is authenticated and `simple` is not set this returns something li "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "created_at": "2013-09-30T13:46:02Z", @@ -371,6 +373,7 @@ This endpoint supports [keyset pagination](README.md#keyset-based-pagination) fo "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "created_at": "2013-09-30T13:46:02Z", @@ -447,6 +450,7 @@ This endpoint supports [keyset pagination](README.md#keyset-based-pagination) fo "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "created_at": "2013-09-30T13:46:02Z", @@ -584,6 +588,7 @@ Example response: "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "created_at": "2013-09-30T13:46:02Z", @@ -657,6 +662,7 @@ Example response: "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "created_at": "2013-09-30T13:46:02Z", @@ -774,6 +780,7 @@ GET /projects/:id "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "container_expiration_policy": { @@ -1260,6 +1267,7 @@ Example responses: "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "created_at": "2013-09-30T13:46:02Z", @@ -1342,6 +1350,7 @@ Example response: "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "created_at": "2013-09-30T13:46:02Z", @@ -1431,6 +1440,7 @@ Example response: "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "created_at": "2013-09-30T13:46:02Z", @@ -1594,6 +1604,7 @@ Example response: "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "created_at": "2013-09-30T13:46:02Z", @@ -1702,6 +1713,7 @@ Example response: "jobs_enabled": true, "wiki_enabled": true, "snippets_enabled": false, + "can_create_merge_request_in": true, "resolve_outdated_diff_discussions": false, "container_registry_enabled": false, "created_at": "2013-09-30T13:46:02Z", diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md index e839ae0ea3a..8ba6cbf657d 100644 --- a/doc/development/contributing/merge_request_workflow.md +++ b/doc/development/contributing/merge_request_workflow.md @@ -15,8 +15,7 @@ to be marked as `Accepting Merge Requests`. Please include screenshots or wireframes of the proposed feature if it will also change the UI. Merge requests should be submitted to the appropriate project at GitLab.com, for example -[GitLab CE](https://gitlab.com/gitlab-org/gitlab-foss/merge_requests), -[GitLab EE](https://gitlab.com/gitlab-org/gitlab/merge_requests), +[GitLab](https://gitlab.com/gitlab-org/gitlab/merge_requests), [GitLab Runner](https://gitlab.com/gitlab-org/gitlab-runner/merge_requests), [GitLab Omnibus](https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests), etc. @@ -227,6 +226,7 @@ requirements. 1. [Changelog entry added](../changelog.md), if necessary. 1. Reviewed by relevant (UX/FE/BE/tech writing) reviewers and all concerns are addressed. 1. Merged by a project maintainer. +1. Create an issue in the [infrastructure issue tracker](https://gitlab.com/gitlab-com/gl-infra/infrastructure/issues) to inform the Infrastructure department when your contribution is changing default settings or introduces a new setting, if relevant. 1. Confirmed to be working in the [Canary stage](https://about.gitlab.com/handbook/engineering/#canary-testing) or on GitLab.com once the contribution is deployed. 1. Added to the [release post](https://about.gitlab.com/handbook/marketing/blog/release-posts/), if relevant. diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index 063f2b74ce2..b361648b2f0 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -771,6 +771,9 @@ nicely on different mobile devices. To make things easier for the user, always add a full code block for things that can be useful to copy and paste, as they can easily do it with the button on code blocks. - Add a blank line above and below code blocks. +- When providing a shell command and its output, prefix the shell command with `$` and + leave a blank line between the command and the output. +- When providing a command without output, don't prefix the shell command with `$`. - For regular code blocks, always use a highlighting class corresponding to the language for better readability. Examples: @@ -795,7 +798,8 @@ nicely on different mobile devices. - To display raw Markdown instead of rendered Markdown, you can use triple backticks with `md`, like the `Markdown code` example above, unless you want to include triple backticks in the code block as well. In that case, use triple tildes (`~~~`) instead. -- [Syntax highlighting for code blocks](https://github.com/rouge-ruby/rouge/wiki/List-of-supported-languages-and-lexers) is available for many languages. +- [Syntax highlighting for code blocks](https://github.com/rouge-ruby/rouge/wiki/List-of-supported-languages-and-lexers) + is available for many languages. Use `shell` instead of `bash` or `sh` for shell output. - For a complete reference on code blocks, check the [Kramdown guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/#code-blocks). ## GitLab SVG icons diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index ff978ee2899..d9b2ace1b5b 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -54,6 +54,17 @@ By default, this seeds an average of 10 issues per week for the last 52 weeks per project. All issues will also be randomly labeled with team, type, severity, and priority. +#### Seeding groups with sub-groups + +You can seed groups with sub-groups that contain milestones/projects/issues +with the `gitlab:seed:group_seed` task: + +```shell +bin/rake "gitlab:seed:group_seed[subgroup_depth, username]" +``` + +Group are additionally seeded with epics if GitLab instance has epics feature available. + ### Automation If you're very sure that you want to **wipe the current database** and refill diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 95d57b49453..32a5f3ce33d 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -132,6 +132,10 @@ module API expose(:jobs_enabled) { |project, options| project.feature_available?(:builds, options[:current_user]) } expose(:snippets_enabled) { |project, options| project.feature_available?(:snippets, options[:current_user]) } + expose(:can_create_merge_request_in) do |project, options| + Ability.allowed?(options[:current_user], :create_merge_request_in, project) + end + expose(:issues_access_level) { |project, options| project.project_feature.string_access_level(:issues) } expose(:repository_access_level) { |project, options| project.project_feature.string_access_level(:repository) } expose(:merge_requests_access_level) { |project, options| project.project_feature.string_access_level(:merge_requests) } diff --git a/lib/tasks/gitlab/seed/group_seed.rake b/lib/tasks/gitlab/seed/group_seed.rake new file mode 100644 index 00000000000..bc705c94422 --- /dev/null +++ b/lib/tasks/gitlab/seed/group_seed.rake @@ -0,0 +1,233 @@ +# frozen_string_literal: true + +# Seed test groups with: +# 1. 2 Subgroups per level +# 1. 2 Users & group members per group +# 1. 2 Epics, 2 Milestones & 2 Projects per group +# 1. Project issues +# +# It also assigns each project's issue with one of group's or ascendants +# groups milestone & epic. +# +# @param subgroups_depth - number of subgroup levels +# @param username - user creating subgroups (i.e. GitLab admin) +# +# @example +# bundle exec rake "gitlab:seed:group_seed[5, root]" +# +namespace :gitlab do + namespace :seed do + desc 'Seed groups with sub-groups/projects/epics/milestones for Group Import testing' + task :group_seed, [:subgroups_depth, :username] => :gitlab_environment do |_t, args| + require 'sidekiq/testing' + + GroupSeeder.new( + subgroups_depth: args.subgroups_depth, + username: args.username + ).seed + end + end +end + +class GroupSeeder + PROJECT_URL = 'https://gitlab.com/gitlab-org/gitlab-test.git' + + attr_reader :all_group_ids + + def initialize(subgroups_depth:, username:) + @subgroups_depth = subgroups_depth.to_i + @user = User.find_by_username(username) + @group_names = Set.new + @resource_count = 2 + @all_groups = {} + @all_group_ids = [] + end + + def seed + create_groups + + puts 'Done!' + end + + def create_groups + create_root_group + create_sub_groups + create_users_and_members + create_epics if Gitlab.ee? + create_labels + create_milestones + + Sidekiq::Testing.inline! do + create_projects + end + end + + def create_users_and_members + all_group_ids.each do |group_id| + @resource_count.times do |_| + user = create_user + create_member(user.id, group_id) + end + end + end + + def create_root_group + root_group = ::Groups::CreateService.new(@user, group_params).execute + + track_group_id(1, root_group.id) + end + + def create_sub_groups + (2..@subgroups_depth).each do |level| + parent_level = level - 1 + current_level = level + parent_groups = @all_groups[parent_level] + + parent_groups.each do |parent_id| + @resource_count.times do |_| + sub_group = ::Groups::CreateService.new(@user, group_params(parent_id: parent_id)).execute + + track_group_id(current_level, sub_group.id) + end + end + end + end + + def track_group_id(depth_level, group_id) + @all_groups[depth_level] ||= [] + @all_groups[depth_level] << group_id + @all_group_ids << group_id + end + + def group_params(parent_id: nil) + name = unique_name + + { + name: name, + path: name, + parent_id: parent_id + } + end + + def unique_name + name = ffaker_name + name = ffaker_name until @group_names.add?(name) + name + end + + def ffaker_name + FFaker::Lorem.characters(5) + end + + def create_user + User.create!( + username: FFaker::Internet.user_name, + name: FFaker::Name.name, + email: FFaker::Internet.email, + confirmed_at: DateTime.now, + password: Devise.friendly_token + ) + end + + def create_member(user_id, group_id) + roles = Gitlab::Access.values + + GroupMember.create(user_id: user_id, access_level: roles.sample, source_id: group_id) + end + + def create_epics + all_group_ids.each do |group_id| + @resource_count.times do |_| + group = Group.find(group_id) + + epic_params = { + title: FFaker::Lorem.sentence(6), + description: FFaker::Lorem.paragraphs(3).join("\n\n"), + author: group.users.sample, + group: group + } + + Epic.create!(epic_params) + end + end + end + + def create_labels + all_group_ids.each do |group_id| + @resource_count.times do |_| + group = Group.find(group_id) + label_title = FFaker::Product.brand + + Labels::CreateService.new(title: label_title, color: "##{Digest::MD5.hexdigest(label_title)[0..5]}").execute(group: group) + end + end + end + + def create_milestones + all_group_ids.each do |group_id| + @resource_count.times do |i| + group = Group.find(group_id) + + milestone_params = { + title: "v#{i}.0", + description: FFaker::Lorem.sentence, + state: [:active, :closed].sample + } + + Milestones::CreateService.new(group, group.members.sample, milestone_params).execute + end + end + end + + def create_projects + all_group_ids.each do |group_id| + group = Group.find(group_id) + + @resource_count.times do |i| + _, project_path = PROJECT_URL.split('/')[-2..-1] + + project_path.gsub!('.git', '') + + params = { + import_url: PROJECT_URL, + namespace_id: group.id, + name: project_path.titleize + FFaker::Lorem.characters(10), + description: FFaker::Lorem.sentence, + visibility_level: 0, + skip_disk_validation: true + } + + project = nil + + Sidekiq::Worker.skipping_transaction_check do + project = ::Projects::CreateService.new(@user, params).execute + project.send(:_run_after_commit_queue) + project.import_state.send(:_run_after_commit_queue) + project.repository.expire_all_method_caches + end + + create_project_issues(project) + assign_issues_to_epics_and_milestones(project) + end + end + end + + def create_project_issues(project) + seeder = Quality::Seeders::Issues.new(project: project) + seeder.seed(backfill_weeks: 2, average_issues_per_week: 2) + end + + def assign_issues_to_epics_and_milestones(project) + group_ids = project.group.self_and_ancestors.map(&:id) + + project.issues.each do |issue| + issue_params = { + milestone: Milestone.where(group: group_ids).sample + } + + issue_params[:epic] = Epic.where(group: group_ids).sample if Gitlab.ee? + + issue.update(issue_params) + end + end +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 81442d3fc7d..adc979efd1d 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -6035,6 +6035,9 @@ msgstr "" msgid "Default project deletion protection" msgstr "" +msgid "Default projects limit" +msgstr "" + msgid "Default: Directly import the Google Code email address or username" msgstr "" @@ -10470,6 +10473,9 @@ msgstr "" msgid "Is blocked by" msgstr "" +msgid "Is this GitLab trial for your company?" +msgstr "" + msgid "Is using license seat:" msgstr "" @@ -11628,6 +11634,9 @@ msgstr "" msgid "Maximum delay (Minutes)" msgstr "" +msgid "Maximum duration of a session." +msgstr "" + msgid "Maximum job timeout" msgstr "" @@ -11646,12 +11655,24 @@ msgstr "" msgid "Maximum number of mirrors that can be synchronizing at the same time." msgstr "" +msgid "Maximum number of projects." +msgstr "" + msgid "Maximum page reached" msgstr "" msgid "Maximum push size (MB)" msgstr "" +msgid "Maximum size limit for a single commit." +msgstr "" + +msgid "Maximum size limit for each repository." +msgstr "" + +msgid "Maximum size of individual attachments in comments." +msgstr "" + msgid "Maximum time between updates that a mirror can have when scheduled to synchronize." msgstr "" @@ -17024,9 +17045,6 @@ msgstr "" msgid "Session duration (minutes)" msgstr "" -msgid "Session expiration, projects limit and attachment size." -msgstr "" - msgid "Set %{epic_ref} as the parent epic." msgstr "" @@ -17066,6 +17084,9 @@ msgstr "" msgid "Set parent epic to an epic" msgstr "" +msgid "Set projects and maximum size limits, session duration, user options, and check feature availability for namespace plan." +msgstr "" + msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication." msgstr "" diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb index 3f3711f9eb8..d3a0c9b790b 100644 --- a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb @@ -83,15 +83,15 @@ describe 'User updates wiki page' do end it 'updates the commit message as the title is changed', :js do - fill_in(:wiki_title, with: 'Wiki title') + fill_in(:wiki_title, with: '& < > \ \ { } &') - expect(page).to have_field('wiki[message]', with: 'Update Wiki title') + expect(page).to have_field('wiki[message]', with: 'Update & < > \ \ { } &') end - it 'does not allow XSS', :js do - fill_in(:wiki_title, with: '<script>') + it 'correctly escapes the commit message entities', :js do + fill_in(:wiki_title, with: 'Wiki title') - expect(page).to have_field('wiki[message]', with: 'Update <script>') + expect(page).to have_field('wiki[message]', with: 'Update Wiki title') end it 'shows a validation error message' do diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb index 77e725e7f11..c7856342fb2 100644 --- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb +++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb @@ -129,6 +129,18 @@ describe 'User views a wiki page' do end end + context 'when a page has XSS in its message' do + before do + wiki_page.update(message: '<script>alert(true)<script>', content: 'XSS update') + end + + it 'safely displays the message' do + visit(project_wiki_history_path(project, wiki_page)) + + expect(page).to have_content('<script>alert(true)<script>') + end + end + context 'when page has invalid content encoding' do let(:content) { (+'whatever').force_encoding('ISO-8859-1') } diff --git a/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap new file mode 100644 index 00000000000..2b9668c1b56 --- /dev/null +++ b/spec/frontend/monitoring/components/__snapshots__/dashboard_template_spec.js.snap @@ -0,0 +1,94 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Dashboard template matches the default snapshot 1`] = ` +<div + class="prometheus-graphs" + data-qa-selector="prometheus_graphs" +> + <div + class="prometheus-graphs-header gl-p-3 pb-0 border-bottom bg-gray-light" + > + <div + class="row" + > + <gl-form-group-stub + class="col-sm-12 col-md-6 col-lg-2" + label="Dashboard" + label-for="monitor-dashboards-dropdown" + label-size="sm" + > + <dashboards-dropdown-stub + class="mb-0 d-flex" + defaultbranch="master" + id="monitor-dashboards-dropdown" + selecteddashboard="[object Object]" + toggle-class="dropdown-menu-toggle" + /> + </gl-form-group-stub> + + <gl-form-group-stub + class="col-sm-6 col-md-6 col-lg-2" + label="Environment" + label-for="monitor-environments-dropdown" + label-size="sm" + > + <gl-dropdown-stub + class="mb-0 d-flex" + data-qa-selector="environments_dropdown" + id="monitor-environments-dropdown" + menu-class="monitor-environment-dropdown-menu" + text="production" + toggle-class="dropdown-menu-toggle" + > + <div + class="d-flex flex-column overflow-hidden" + > + <gl-dropdown-header-stub + class="text-center" + > + Environment + </gl-dropdown-header-stub> + + <gl-dropdown-divider-stub /> + + <!----> + + <div + class="flex-fill overflow-auto" + /> + + <!----> + </div> + </gl-dropdown-stub> + </gl-form-group-stub> + + <gl-form-group-stub + class="col-sm-6 col-md-6 col-lg-4" + label="Show last" + label-for="monitor-time-window-dropdown" + label-size="sm" + > + <date-time-picker-stub + end="2020-01-01T18:57:47.000Z" + start="2020-01-01T18:27:47.000Z" + timewindows="[object Object]" + /> + </gl-form-group-stub> + + <!----> + </div> + </div> + + <empty-state-stub + clusterspath="/path/to/clusters" + documentationpath="/path/to/docs" + emptygettingstartedsvgpath="/path/to/getting-started.svg" + emptyloadingsvgpath="/path/to/loading.svg" + emptynodatasmallsvgpath="/path/to/no-data-small.svg" + emptynodatasvgpath="/path/to/no-data.svg" + emptyunabletoconnectsvgpath="/path/to/unable-to-connect.svg" + selectedstate="gettingStarted" + settingspath="/path/to/settings" + /> +</div> +`; diff --git a/spec/frontend/monitoring/components/dashboard_template_spec.js b/spec/frontend/monitoring/components/dashboard_template_spec.js new file mode 100644 index 00000000000..d525f4821f4 --- /dev/null +++ b/spec/frontend/monitoring/components/dashboard_template_spec.js @@ -0,0 +1,45 @@ +import { shallowMount } from '@vue/test-utils'; +import MockAdapter from 'axios-mock-adapter'; +import axios from '~/lib/utils/axios_utils'; +import Dashboard from '~/monitoring/components/dashboard.vue'; +import { createStore } from '~/monitoring/stores'; +import { propsData } from '../init_utils'; + +jest.mock('~/lib/utils/url_utility', () => ({ + getParameterValues: jest.fn().mockImplementation(param => { + if (param === 'start') return ['2020-01-01T18:27:47.000Z']; + if (param === 'end') return ['2020-01-01T18:57:47.000Z']; + return []; + }), +})); + +describe('Dashboard template', () => { + let wrapper; + let store; + let mock; + + beforeEach(() => { + store = createStore(); + mock = new MockAdapter(axios); + }); + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + wrapper = null; + } + mock.restore(); + }); + + it('matches the default snapshot', () => { + wrapper = shallowMount(Dashboard, { + propsData: { ...propsData }, + methods: { + fetchData: jest.fn(), + }, + store, + }); + + expect(wrapper.element).toMatchSnapshot(); + }); +}); diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index 6ac7b4af452..e88209081d4 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -1340,6 +1340,7 @@ describe API::Projects do expect(json_response['path']).to be_present expect(json_response['issues_enabled']).to be_present expect(json_response['merge_requests_enabled']).to be_present + expect(json_response['can_create_merge_request_in']).to be_present expect(json_response['wiki_enabled']).to be_present expect(json_response['jobs_enabled']).to be_present expect(json_response['snippets_enabled']).to be_present @@ -1390,6 +1391,7 @@ describe API::Projects do expect(json_response['path']).to be_present expect(json_response['issues_enabled']).to be_present expect(json_response['merge_requests_enabled']).to be_present + expect(json_response['can_create_merge_request_in']).to be_present expect(json_response['wiki_enabled']).to be_present expect(json_response['jobs_enabled']).to be_present expect(json_response['snippets_enabled']).to be_present diff --git a/spec/tasks/gitlab/seed/group_seed_rake_spec.rb b/spec/tasks/gitlab/seed/group_seed_rake_spec.rb new file mode 100644 index 00000000000..bc217281594 --- /dev/null +++ b/spec/tasks/gitlab/seed/group_seed_rake_spec.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +require 'rake_helper' + +describe 'gitlab:seed:group_seed rake task', :sidekiq do + let(:username) { 'group_seed' } + let!(:user) { create(:user, username: username) } + let(:task_params) { [2, username] } + + before do + Rake.application.rake_require('tasks/gitlab/seed/group_seed') + end + + subject { run_rake_task('gitlab:seed:group_seed', task_params) } + + it 'performs group seed successfully' do + expect { subject }.not_to raise_error + + group = user.groups.first + + expect(user.groups.count).to be 3 + expect(group.projects.count).to be 2 + expect(group.members.count).to be 3 + expect(group.milestones.count).to be 2 + end +end |