{{ $options.TEMPORARY_PLACEHOLDER }}
+ diff --git a/app/assets/javascripts/nav/event_hub.js b/app/assets/javascripts/nav/event_hub.js new file mode 100644 index 00000000000..2c8b1371fe3 --- /dev/null +++ b/app/assets/javascripts/nav/event_hub.js @@ -0,0 +1,5 @@ +import eventHubFactory from '~/helpers/event_hub_factory'; + +export const EVENT_RESPONSIVE_TOGGLE = 'top-nav-responsive-toggle'; + +export default eventHubFactory(); diff --git a/app/assets/javascripts/nav/index.js b/app/assets/javascripts/nav/index.js index 646ce3f0ecf..86d6b42e4ea 100644 --- a/app/assets/javascripts/nav/index.js +++ b/app/assets/javascripts/nav/index.js @@ -1,12 +1,28 @@ -export const initTopNav = async () => { +// With combined_menu feature flag, there's a benefit to splitting up the import +const importModule = () => import(/* webpackChunkName: 'top_nav' */ './mount'); + +const tryMountTopNav = async () => { const el = document.getElementById('js-top-nav'); if (!el) { return; } - // With combined_menu feature flag, there's a benefit to splitting up the import - const { mountTopNav } = await import(/* webpackChunkName: 'top_nav' */ './mount'); + const { mountTopNav } = await importModule(); mountTopNav(el); }; + +const tryMountTopNavResponsive = async () => { + const el = document.getElementById('js-top-nav-responsive'); + + if (!el) { + return; + } + + const { mountTopNavResponsive } = await importModule(); + + mountTopNavResponsive(el); +}; + +export const initTopNav = async () => Promise.all([tryMountTopNav(), tryMountTopNavResponsive()]); diff --git a/app/assets/javascripts/nav/mount.js b/app/assets/javascripts/nav/mount.js index 0d46ff56249..51b6a31b8cb 100644 --- a/app/assets/javascripts/nav/mount.js +++ b/app/assets/javascripts/nav/mount.js @@ -1,11 +1,12 @@ import Vue from 'vue'; import Vuex from 'vuex'; +import ResponsiveApp from './components/responsive_app.vue'; import App from './components/top_nav_app.vue'; import { createStore } from './stores'; Vue.use(Vuex); -export const mountTopNav = (el) => { +const mount = (el, Component) => { const viewModel = JSON.parse(el.dataset.viewModel); const store = createStore(); @@ -13,7 +14,7 @@ export const mountTopNav = (el) => { el, store, render(h) { - return h(App, { + return h(Component, { props: { navData: viewModel, }, @@ -21,3 +22,7 @@ export const mountTopNav = (el) => { }, }); }; + +export const mountTopNav = (el) => mount(el, App); + +export const mountTopNavResponsive = (el) => mount(el, ResponsiveApp); diff --git a/app/assets/javascripts/profile/gl_crop.js b/app/assets/javascripts/profile/gl_crop.js index afc78cbe78a..722f7d467a2 100644 --- a/app/assets/javascripts/profile/gl_crop.js +++ b/app/assets/javascripts/profile/gl_crop.js @@ -114,7 +114,9 @@ import { loadCSSFile } from '../lib/utils/css_utils'; } onModalHide() { - return this.modalCropImg.attr('src', '').cropper('destroy'); + this.modalCropImg.attr('src', '').cropper('destroy'); + const modalElement = document.querySelector('.modal-profile-crop'); + if (modalElement) modalElement.remove(); } onUploadImageBtnClick(e) { diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss index 7566a533911..8639b9a7f84 100644 --- a/app/assets/stylesheets/framework/header.scss +++ b/app/assets/stylesheets/framework/header.scss @@ -106,7 +106,7 @@ $top-nav-hover-bg: var(--indigo-900-alpha-008, $indigo-900-alpha-008) !important &.menu-expanded { @include media-breakpoint-down(xs) { - .title-container { + .hide-when-menu-expanded { display: none; } @@ -665,3 +665,26 @@ $top-nav-hover-bg: var(--indigo-900-alpha-008, $indigo-900-alpha-008) !important color: inherit !important; } } + +.top-nav-responsive { + @include gl-display-none; + color: var(--indigo-900, $theme-indigo-900); +} + +.top-nav-responsive-open { + .hide-when-top-nav-responsive-open { + @include media-breakpoint-down(xs) { + display: none !important; + } + } + + .top-nav-responsive { + @include media-breakpoint-down(xs) { + @include gl-display-block; + } + } + + .navbar-gitlab .header-content .title-container { + flex: 0; + } +} diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss index dafa89465d1..d5e349ab72c 100644 --- a/app/assets/stylesheets/startup/startup-dark.scss +++ b/app/assets/stylesheets/startup/startup-dark.scss @@ -337,9 +337,6 @@ h1 { .d-none { display: none !important; } -.d-inline-block { - display: inline-block !important; -} .d-block { display: block !important; } @@ -354,9 +351,6 @@ h1 { } } @media (min-width: 992px) { - .d-lg-none { - display: none !important; - } .d-lg-block { display: block !important; } @@ -1957,9 +1951,7 @@ body.gl-dark .navbar-gitlab .navbar-collapse { } body.gl-dark .navbar-gitlab .container-fluid .navbar-toggler { border-left: 1px solid #b3b3b3; -} -body.gl-dark .navbar-gitlab .container-fluid .navbar-toggler svg { - fill: #fafafa; + color: #fafafa; } body.gl-dark .navbar-gitlab .navbar-sub-nav > li.active > a, body.gl-dark .navbar-gitlab .navbar-sub-nav > li.active > button, @@ -2146,9 +2138,40 @@ body.gl-dark { white-space: nowrap; width: 1px; } +.gl-border-none\! { + border-style: none !important; +} +.gl-display-none { + display: none; +} +@media (min-width: 62rem) { + .gl-lg-display-none { + display: none; + } +} +@media (min-width: 36rem) { + .gl-sm-display-block { + display: block; + } +} +.gl-display-inline-block { + display: inline-block; +} +@media (min-width: 36rem) { + .gl-sm-display-inline-block { + display: inline-block; + } +} .gl-absolute { position: absolute; } +.gl-px-3 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} +.gl-pr-2 { + padding-right: 0.25rem; +} .gl-ml-3 { margin-left: 0.5rem; } diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss index c7020e8af55..9f15b107bc7 100644 --- a/app/assets/stylesheets/startup/startup-general.scss +++ b/app/assets/stylesheets/startup/startup-general.scss @@ -322,9 +322,6 @@ h1 { .d-none { display: none !important; } -.d-inline-block { - display: inline-block !important; -} .d-block { display: block !important; } @@ -339,9 +336,6 @@ h1 { } } @media (min-width: 992px) { - .d-lg-none { - display: none !important; - } .d-lg-block { display: block !important; } @@ -1927,9 +1921,40 @@ body.sidebar-refactoring white-space: nowrap; width: 1px; } +.gl-border-none\! { + border-style: none !important; +} +.gl-display-none { + display: none; +} +@media (min-width: 62rem) { + .gl-lg-display-none { + display: none; + } +} +@media (min-width: 36rem) { + .gl-sm-display-block { + display: block; + } +} +.gl-display-inline-block { + display: inline-block; +} +@media (min-width: 36rem) { + .gl-sm-display-inline-block { + display: inline-block; + } +} .gl-absolute { position: absolute; } +.gl-px-3 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} +.gl-pr-2 { + padding-right: 0.25rem; +} .gl-ml-3 { margin-left: 0.5rem; } diff --git a/app/assets/stylesheets/themes/theme_helper.scss b/app/assets/stylesheets/themes/theme_helper.scss index 5b3e2ab4cd0..6a60978b954 100644 --- a/app/assets/stylesheets/themes/theme_helper.scss +++ b/app/assets/stylesheets/themes/theme_helper.scss @@ -22,10 +22,7 @@ .container-fluid { .navbar-toggler { border-left: 1px solid lighten($border-and-box-shadow, 10%); - - svg { - fill: $search-and-nav-links; - } + color: $search-and-nav-links; } } diff --git a/app/models/integrations/slack.rb b/app/models/integrations/slack.rb index a83fd3bcbeb..0381db3a67e 100644 --- a/app/models/integrations/slack.rb +++ b/app/models/integrations/slack.rb @@ -25,7 +25,7 @@ module Integrations end def default_channel_placeholder - _('general, development') + _('#general, #development') end def webhook_placeholder diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml index 61f03c0540d..2b63e2c647c 100644 --- a/app/views/layouts/_page.html.haml +++ b/app/views/layouts/_page.html.haml @@ -1,4 +1,4 @@ -.layout-page{ class: page_with_sidebar_class } +.layout-page.hide-when-top-nav-responsive-open{ class: page_with_sidebar_class } - if defined?(nav) && nav = render "layouts/nav/sidebar/#{nav}" .content-wrapper.content-wrapper-margin{ class: "#{@content_wrapper_class}" } @@ -27,3 +27,5 @@ = render "layouts/flash", extra_flash_class: 'limit-container-width' = yield :before_content = yield + += render "layouts/nav/top_nav_responsive", class: 'layout-page content-wrapper-margin' diff --git a/app/views/layouts/fullscreen.html.haml b/app/views/layouts/fullscreen.html.haml index f46c58f96ee..b2536090520 100644 --- a/app/views/layouts/fullscreen.html.haml +++ b/app/views/layouts/fullscreen.html.haml @@ -6,11 +6,12 @@ = header_message = render partial: "layouts/header/default", locals: { project: @project, group: @group } .mobile-overlay - .alert-wrapper + .alert-wrapper.hide-when-top-nav-responsive-open = render 'shared/outdated_browser' = render "layouts/broadcast" = yield :flash_message = render "layouts/flash" - .content-wrapper{ id: "content-body", class: "d-flex flex-column align-items-stretch" } + .content-wrapper.hide-when-top-nav-responsive-open{ id: "content-body", class: "d-flex flex-column align-items-stretch" } = yield + = render "layouts/nav/top_nav_responsive", class: "gl-flex-fill-1 gl-overflow-y-auto" = footer_message diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index ae333cffb84..a0f6f35a5be 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -1,11 +1,12 @@ - has_impersonation_link = header_link?(:admin_impersonation) - user_status_data = user_status_properties(current_user) +- use_top_nav_redesign = Feature.enabled?(:combined_menu, current_user, default_enabled: :yaml) %header.navbar.navbar-gitlab.navbar-expand-sm.js-navbar{ data: { qa_selector: 'navbar' } } %a.gl-sr-only.gl-accessibility{ href: "#content-body" } Skip to content .container-fluid .header-content - .title-container + .title-container{ class: ('hide-when-menu-expanded' if !use_top_nav_redesign) } %h1.title %span.gl-sr-only GitLab = link_to root_path, title: _('Dashboard'), id: 'logo', **tracking_attrs('main_navigation', 'click_gitlab_logo_link', 'navigation') do @@ -19,8 +20,9 @@ %span.gl-badge.gl-bg-green-500.gl-text-white.gl-rounded-pill.gl-font-weight-bold.gl-py-1 = _('Next') - - if Feature.enabled?(:combined_menu, current_user, default_enabled: :yaml) - = render "layouts/nav/top_nav" + - if use_top_nav_redesign + .gl-display-none.gl-sm-display-block + = render "layouts/nav/top_nav" - else - if current_user = render "layouts/nav/dashboard" @@ -30,11 +32,11 @@ .navbar-collapse.collapse %ul.nav.navbar-nav - if current_user - = render 'layouts/header/new_dropdown' + = render 'layouts/header/new_dropdown', class: ('gl-display-none gl-sm-display-block' if use_top_nav_redesign) - if header_link?(:search) %li.nav-item.d-none.d-lg-block.m-auto = render 'layouts/search' unless current_controller?(:search) - %li.nav-item.d-inline-block.d-lg-none + %li.nav-item{ class: use_top_nav_redesign ? "gl-display-none gl-sm-display-inline-block gl-lg-display-none" : "gl-display-inline-block gl-lg-display-none" } = link_to search_context.search_url, title: _('Search'), aria: { label: _('Search') }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do = sprite_icon('search') - if header_link?(:issues) @@ -115,10 +117,15 @@ - sign_in_text = allow_signup? ? _('Sign in / Register') : _('Sign in') = link_to sign_in_text, new_session_path(:user, redirect_to_referer: 'yes'), class: 'gl-button btn btn-default btn-sign-in' - %button.navbar-toggler.d-block.d-sm-none{ type: 'button' } + %button.navbar-toggler.d-block.d-sm-none{ type: 'button', class: ('gl-border-none!' if use_top_nav_redesign) } %span.sr-only= _('Toggle navigation') - = sprite_icon('ellipsis_h', size: 12, css_class: 'more-icon js-navbar-toggle-right') - = sprite_icon('close', size: 12, css_class: 'close-icon js-navbar-toggle-left') + - if use_top_nav_redesign + %span.more-icon.gl-px-3 + %span.gl-pr-2= _('Menu') + = sprite_icon('dot-grid', size: 16) + - else + = sprite_icon('ellipsis_h', size: 12, css_class: 'more-icon') + = sprite_icon('close', size: 12, css_class: 'close-icon') - if display_whats_new? #whats-new-app{ data: { version_digest: whats_new_version_digest } } diff --git a/app/views/layouts/header/_new_dropdown.html.haml b/app/views/layouts/header/_new_dropdown.html.haml index 47846207978..c5f43fb2c16 100644 --- a/app/views/layouts/header/_new_dropdown.html.haml +++ b/app/views/layouts/header/_new_dropdown.html.haml @@ -2,10 +2,11 @@ - menu_sections = view_model.fetch(:menu_sections) - title = view_model.fetch(:title) - show_headers = menu_sections.length > 1 +- top_class = local_assigns.fetch(:class, nil) - return if menu_sections.empty? -%li.header-new.dropdown{ data: { track_label: "new_dropdown", track_event: "click_dropdown", track_experiment: "new_repo" } } +%li.header-new.dropdown{ class: top_class, data: { track_label: "new_dropdown", track_event: "click_dropdown", track_experiment: "new_repo" } } = link_to new_project_path, class: "header-new-dropdown-toggle has-tooltip", id: "js-onboarding-new-project-link", title: title, ref: 'tooltip', aria: { label: title }, data: { toggle: 'dropdown', placement: 'bottom', container: 'body', display: 'static', qa_selector: 'new_menu_toggle' } do = sprite_icon('plus-square') = sprite_icon('chevron-down', css_class: 'caret-down') diff --git a/app/views/layouts/nav/_top_nav_responsive.html.haml b/app/views/layouts/nav/_top_nav_responsive.html.haml new file mode 100644 index 00000000000..701bc603391 --- /dev/null +++ b/app/views/layouts/nav/_top_nav_responsive.html.haml @@ -0,0 +1,7 @@ +- return unless Feature.enabled?(:combined_menu, current_user, default_enabled: :yaml) + +- top_class = local_assigns.fetch(:class, nil) +- view_model = top_nav_view_model(project: @project, group: @group) + +.top-nav-responsive{ class: top_class } + #js-top-nav-responsive{ data: { view_model: view_model.to_json } } diff --git a/db/post_migrate/20210526222715_backfill_draft_status_on_merge_requests.rb b/db/post_migrate/20210526222715_backfill_draft_status_on_merge_requests.rb index 300c180acc3..8ff0e306ad5 100644 --- a/db/post_migrate/20210526222715_backfill_draft_status_on_merge_requests.rb +++ b/db/post_migrate/20210526222715_backfill_draft_status_on_merge_requests.rb @@ -1,30 +1,17 @@ # frozen_string_literal: true class BackfillDraftStatusOnMergeRequests < ActiveRecord::Migration[6.0] - include Gitlab::Database::MigrationHelpers - - DOWNTIME = false - - INDEX_NAME = "tmp_index_merge_requests_draft_and_status" - - disable_ddl_transaction! + # include Gitlab::Database::MigrationHelpers + # Marking these as no-op as the original contents caused timeouts on + # staging. Removing the code here per + # #https://docs.gitlab.com/ee/development/deleting_migrations.html#how-to-disable-a-data-migration + # => def up - add_concurrent_index :merge_requests, :id, - where: "draft = false AND state_id = 1 AND ((title)::text ~* '^\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP'::text)", - name: INDEX_NAME - - update_column_in_batches(:merge_requests, :draft, true, batch_size: 100) do |table, query| - query - .where(table[:state_id].eq(1)) - .where(table[:draft].eq(false)) - .where(table[:title].matches_regexp('^\\[draft\\]|\\(draft\\)|draft:|draft|\\[WIP\\]|WIP:|WIP', false)) - end - - remove_concurrent_index_by_name :merge_requests, INDEX_NAME + # no-op end def down - remove_concurrent_index_by_name :merge_requests, INDEX_NAME + # no-op end end diff --git a/doc/administration/configure.md b/doc/administration/configure.md new file mode 100644 index 00000000000..12a8f721ccf --- /dev/null +++ b/doc/administration/configure.md @@ -0,0 +1,16 @@ +--- +stage: Enablement +group: Distribution +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +type: reference +--- + +# Configure your GitLab installation + +Customize and configure your self-managed GitLab installation. + +- [Authentication](auth/README.md) +- [Configuration](../user/admin_area/index.md) +- [Repository storage](repository_storage_paths.md) +- [Geo](geo/index.md) +- [Packages](packages/index.md) diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md index 8e8df73e581..6177932937c 100644 --- a/doc/administration/gitaly/index.md +++ b/doc/administration/gitaly/index.md @@ -403,18 +403,27 @@ GitLab recommends: We welcome your feedback on this process: raise a support ticket, or [comment on the epic](https://gitlab.com/groups/gitlab-org/-/epics/4916). -## Troubleshooting Gitaly +## Troubleshooting -Check [Gitaly timeouts](../../user/admin_area/settings/gitaly_timeouts.md) when troubleshooting -Gitaly. +Refer to the information below when troubleshooting Gitaly and Gitaly Cluster. -### Check versions when using standalone Gitaly servers +### Troubleshoot Gitaly + +The following sections provide possible solutions to Gitaly errors. + +See also: + +- [Gitaly timeout](../../user/admin_area/settings/gitaly_timeouts.md) settings. +- [Gitaly troubleshooting information](../reference_architectures/troubleshooting.md#troubleshooting-gitaly) + in reference architecture documentation. + +#### Check versions when using standalone Gitaly servers When using standalone Gitaly servers, you must make sure they are the same version as GitLab to ensure full compatibility. Check **Admin Area > Overview > Gitaly Servers** on your GitLab instance and confirm all Gitaly servers indicate that they are up to date. -### `gitaly-debug` +#### `gitaly-debug` The `gitaly-debug` command provides "production debugging" tools for Gitaly and Git performance. It is intended to help production engineers and support @@ -437,7 +446,7 @@ To see the help page of `gitaly-debug` for a list of supported sub-commands, run gitaly-debug -h ``` -### Commits, pushes, and clones return a 401 +#### Commits, pushes, and clones return a 401 ```plaintext remote: GitLab: 401 Unauthorized @@ -446,7 +455,7 @@ remote: GitLab: 401 Unauthorized You need to sync your `gitlab-secrets.json` file with your Gitaly clients (GitLab app nodes). -### Client side gRPC logs +#### Client side gRPC logs Gitaly uses the [gRPC](https://grpc.io/) RPC framework. The Ruby gRPC client has its own log file which may contain debugging information when @@ -460,7 +469,7 @@ You can run a gRPC trace with: sudo GRPC_TRACE=all GRPC_VERBOSITY=DEBUG gitlab-rake gitlab:gitaly:check ``` -### Server side gRPC logs +#### Server side gRPC logs gRPC tracing can also be enabled in Gitaly itself with the `GODEBUG=http2debug` environment variable. To set this in an Omnibus GitLab install: @@ -475,7 +484,7 @@ environment variable. To set this in an Omnibus GitLab install: 1. [Reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure) GitLab. -### Correlating Git processes with RPCs +#### Correlating Git processes with RPCs Sometimes you need to find out which Gitaly RPC created a particular Git process. @@ -493,7 +502,7 @@ sudo cat /proc/$PID/environ | tr '\0' '\n' | grep ^CORRELATION_ID= This method isn't reliable for `git cat-file` processes, because Gitaly internally pools and re-uses those across RPCs. -### Observing `gitaly-ruby` traffic +#### Observing `gitaly-ruby` traffic [`gitaly-ruby`](configure_gitaly.md#gitaly-ruby) is an internal implementation detail of Gitaly, so, there's not that much visibility into what goes on inside @@ -514,7 +523,7 @@ implemented as calls to `gitaly-ruby`: sum(rate(grpc_client_handled_total[5m])) by (grpc_method) > 0 ``` -### Repository changes fail with a `401 Unauthorized` error +#### Repository changes fail with a `401 Unauthorized` error If you run Gitaly on its own server and notice these conditions: @@ -609,7 +618,7 @@ on the Gitaly server matches the one on Gitaly client. If it doesn't match, update the secrets file on the Gitaly server to match the Gitaly client, then [reconfigure](../restart_gitlab.md#omnibus-gitlab-reconfigure). -### Command line tools cannot connect to Gitaly +#### Command line tools cannot connect to Gitaly gRPC cannot reach your Gitaly server if: @@ -646,7 +655,7 @@ unset http_proxy unset https_proxy ``` -### Permission denied errors appearing in Gitaly or Praefect logs when accessing repositories +#### Permission denied errors appearing in Gitaly or Praefect logs when accessing repositories You might see the following in Gitaly and Praefect logs: @@ -667,9 +676,71 @@ This is a GRPC call [error response code](https://grpc.github.io/grpc/core/md_doc_statuscodes.html). If this error occurs, even though -[the Gitaly auth tokens are correctly setup](../gitaly/praefect.md#debugging-praefect), +[the Gitaly auth tokens are set up correctly](#praefect-errors-in-logs), it's likely that the Gitaly servers are experiencing [clock drift](https://en.wikipedia.org/wiki/Clock_drift). Ensure the Gitaly clients and servers are synchronized, and use an NTP time server to keep them synchronized. + +### Troubleshoot Praefect (Gitaly Cluster) + +The following sections provide possible solutions to Gitaly Cluster errors. + +#### Praefect errors in logs + +If you receive an error, check `/var/log/gitlab/gitlab-rails/production.log`. + +Here are common errors and potential causes: + +- 500 response code + - **ActionView::Template::Error (7:permission denied)** + - `praefect['auth_token']` and `gitlab_rails['gitaly_token']` do not match on the GitLab server. + - **Unable to save project. Error: 7:permission denied** + - Secret token in `praefect['storage_nodes']` on GitLab server does not match the + value in `gitaly['auth_token']` on one or more Gitaly servers. +- 503 response code + - **GRPC::Unavailable (14:failed to connect to all addresses)** + - GitLab was unable to reach Praefect. + - **GRPC::Unavailable (14:all SubCons are in TransientFailure...)** + - Praefect cannot reach one or more of its child Gitaly nodes. Try running + the Praefect connection checker to diagnose. + +#### Determine primary Gitaly node + +To determine the current primary Gitaly node for a specific Praefect node: + +- Use the `Shard Primary Election` [Grafana chart](praefect.md#grafana) on the + [`Gitlab Omnibus - Praefect` dashboard](https://gitlab.com/gitlab-org/grafana-dashboards/-/blob/master/omnibus/praefect.json). + This is recommended. +- If you do not have Grafana set up, use the following command on each host of each + Praefect node: + + ```shell + curl localhost:9652/metrics | grep gitaly_praefect_primaries` + ``` + +#### Relation does not exist errors + +By default Praefect database tables are created automatically by `gitlab-ctl reconfigure` task. +However, if the `gitlab-ctl reconfigure` command isn't executed or there are errors during the +execution, the Praefect database tables are not created on initial reconfigure and can throw +errors that relations do not exist. + +For example: + +- `ERROR: relation "node_status" does not exist at character 13` +- `ERROR: relation "replication_queue_lock" does not exist at character 40` +- This error: + + ```json + {"level":"error","msg":"Error updating node: pq: relation \"node_status\" does not exist","pid":210882,"praefectName":"gitlab1x4m:0.0.0.0:2305","time":"2021-04-01T19:26:19.473Z","virtual_storage":"praefect-cluster-1"} + ``` + +To solve this, the database schema migration can be done using `sql-migrate` subcommand of +the `praefect` command: + +```shell +$ sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate +praefect sql-migrate: OK (applied 21 migrations) +``` diff --git a/doc/administration/gitaly/praefect.md b/doc/administration/gitaly/praefect.md index 613899df5cb..def2ca0d6ef 100644 --- a/doc/administration/gitaly/praefect.md +++ b/doc/administration/gitaly/praefect.md @@ -162,32 +162,7 @@ node, using `psql` which is installed by Omnibus GitLab. The database used by Praefect is now configured. If you see Praefect database errors after configuring PostgreSQL, see -[troubleshooting steps below](#relation-does-not-exist-errors). - -#### Relation does not exist errors - -By default Praefect database tables are created automatically by `gitlab-ctl reconfigure` task. -However, if the `gitlab-ctl reconfigure` command isn't executed or there are errors during the -execution, the Praefect database tables are not created on initial reconfigure and can throw -errors that relations do not exist. - -For example: - -- `ERROR: relation "node_status" does not exist at character 13` -- `ERROR: relation "replication_queue_lock" does not exist at character 40` -- This error: - - ```json - {"level":"error","msg":"Error updating node: pq: relation \"node_status\" does not exist","pid":210882,"praefectName":"gitlab1x4m:0.0.0.0:2305","time":"2021-04-01T19:26:19.473Z","virtual_storage":"praefect-cluster-1"} - ``` - -To solve this, the database schema migration can be done using `sql-migrate` subcommand of -the `praefect` command: - -```shell -$ sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.toml sql-migrate -praefect sql-migrate: OK (applied 21 migrations) -``` +[troubleshooting steps](index.md#relation-does-not-exist-errors). #### PgBouncer @@ -906,7 +881,7 @@ Particular attention should be shown to: When adding Gitaly Cluster to an existing Gitaly instance, the existing Gitaly storage must use a TCP address. If `gitaly_address` is not specified, then a Unix socket is used, -which will prevent the communication with the cluster. +which prevents the communication with the cluster. For example: @@ -1578,35 +1553,3 @@ After creating and configuring Gitaly Cluster: ``` 1. Repeat for each storage as required. - -## Debugging Praefect - -If you receive an error, check `/var/log/gitlab/gitlab-rails/production.log`. - -Here are common errors and potential causes: - -- 500 response code - - **ActionView::Template::Error (7:permission denied)** - - `praefect['auth_token']` and `gitlab_rails['gitaly_token']` do not match on the GitLab server. - - **Unable to save project. Error: 7:permission denied** - - Secret token in `praefect['storage_nodes']` on GitLab server does not match the - value in `gitaly['auth_token']` on one or more Gitaly servers. -- 503 response code - - **GRPC::Unavailable (14:failed to connect to all addresses)** - - GitLab was unable to reach Praefect. - - **GRPC::Unavailable (14:all SubCons are in TransientFailure...)** - - Praefect cannot reach one or more of its child Gitaly nodes. Try running - the Praefect connection checker to diagnose. - -### Determine primary Gitaly node - -To determine the current primary Gitaly node for a specific Praefect node: - -- Use the `Shard Primary Election` [Grafana chart](#grafana) on the [`Gitlab Omnibus - Praefect` dashboard](https://gitlab.com/gitlab-org/grafana-dashboards/-/blob/master/omnibus/praefect.json). - This is recommended. -- If you do not have Grafana set up, use the following command on each host of each - Praefect node: - - ```shell - curl localhost:9652/metrics | grep gitaly_praefect_primaries` - ``` diff --git a/doc/user/project/clusters/protect/container_host_security/index.md b/doc/user/project/clusters/protect/container_host_security/index.md index 33121c0dd52..5e4df6009f0 100644 --- a/doc/user/project/clusters/protect/container_host_security/index.md +++ b/doc/user/project/clusters/protect/container_host_security/index.md @@ -4,7 +4,7 @@ group: Container Security info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers --- -# Container Host Security +# Container Host Security **(FREE)** Container Host Security in GitLab provides Intrusion Detection and Prevention capabilities that can monitor and (optionally) block activity inside the containers themselves. This is done by leveraging diff --git a/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md b/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md index 456036fc926..b999ae5b933 100644 --- a/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md +++ b/doc/user/project/clusters/protect/container_host_security/quick_start_guide.md @@ -4,7 +4,7 @@ group: Container Security info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers --- -# Getting started with Container Host Security +# Getting started with Container Host Security **(FREE)** The following steps are recommended for installing Container Host Security. diff --git a/doc/user/project/clusters/protect/container_network_security/index.md b/doc/user/project/clusters/protect/container_network_security/index.md index 461b44a77ef..3daa48e1811 100644 --- a/doc/user/project/clusters/protect/container_network_security/index.md +++ b/doc/user/project/clusters/protect/container_network_security/index.md @@ -4,7 +4,7 @@ group: Container Security info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers --- -# Container Network Security +# Container Network Security **(FREE)** Container Network Security in GitLab provides basic firewall functionality by leveraging Cilium NetworkPolicies to filter traffic going in and out of the cluster as well as traffic between pods diff --git a/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md b/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md index 16eb86d74e2..3acb44c61ac 100644 --- a/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md +++ b/doc/user/project/clusters/protect/container_network_security/quick_start_guide.md @@ -4,7 +4,7 @@ group: Container Security info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers --- -# Getting started with Container Network Security +# Getting started with Container Network Security **(FREE)** The following steps are recommended for installing Container Network Security. @@ -58,7 +58,7 @@ use both methods simultaneously, when the application project pipeline runs the NetworkPolicy in the `auto-deploy-values.yaml` file may override policies configured in the UI editor. -## Monitoring throughput `**(ULTIMATE)**` +## Monitoring throughput **(ULTIMATE)** To view statistics for Container Network Security, you must follow the installation steps above and configure GitLab integration with Prometheus. Also, if you use custom Helm values for Cilium, you diff --git a/doc/user/project/clusters/protect/index.md b/doc/user/project/clusters/protect/index.md index 7b43ff316a4..1314a1948d5 100644 --- a/doc/user/project/clusters/protect/index.md +++ b/doc/user/project/clusters/protect/index.md @@ -4,7 +4,7 @@ group: Container Security info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers --- -# Protecting your deployed applications +# Protecting your deployed applications **(FREE)** GitLab makes it straightforward to protect applications deployed in [connected Kubernetes clusters](index.md). These protections are available in the Kubernetes network layer and in the container itself. At diff --git a/doc/user/project/merge_requests/accessibility_testing.md b/doc/user/project/merge_requests/accessibility_testing.md index 061ba1ddbf3..76aff18b00d 100644 --- a/doc/user/project/merge_requests/accessibility_testing.md +++ b/doc/user/project/merge_requests/accessibility_testing.md @@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: reference, howto --- -# Accessibility Testing +# Accessibility testing **(FREE)** > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25144) in GitLab 12.8. @@ -21,7 +21,7 @@ measuring the accessibility of web sites, and has built a simple This job outputs accessibility violations, warnings, and notices for each page analyzed to a file called `accessibility`. -## Accessibility Merge Request widget +## Accessibility merge request widget > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/39425) in GitLab 13.0 behind the disabled [feature flag](../../../administration/feature_flags.md) `:accessibility_report_view`. > - [Feature Flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/217372) in GitLab 13.1. @@ -29,7 +29,7 @@ analyzed to a file called `accessibility`. In addition to the report artifact that is created, GitLab will also show the Accessibility Report in the merge request widget area: -![Accessibility Merge Request Widget](img/accessibility_mr_widget_v13_0.png) +![Accessibility merge request widget](img/accessibility_mr_widget_v13_0.png) ## Configure Accessibility Testing diff --git a/doc/user/project/merge_requests/test_coverage_visualization.md b/doc/user/project/merge_requests/test_coverage_visualization.md index c240aff1857..e044d50d246 100644 --- a/doc/user/project/merge_requests/test_coverage_visualization.md +++ b/doc/user/project/merge_requests/test_coverage_visualization.md @@ -5,7 +5,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w type: reference, howto --- -# Test Coverage Visualization +# Test coverage visualization **(FREE)** > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/3708) in GitLab 12.9. > - [Feature flag removed](https://gitlab.com/gitlab-org/gitlab/-/issues/249811) in GitLab 13.5. diff --git a/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md b/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md index cc0be389891..55e122dec76 100644 --- a/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md +++ b/doc/user/project/merge_requests/testing_and_reports_in_merge_requests.md @@ -6,7 +6,7 @@ type: index description: "Test your code and display reports in merge requests" --- -# Tests and reports in merge requests +# Tests and reports in merge requests **(FREE)** GitLab has the ability to test the changes included in a feature branch and display reports or link to useful information directly from merge requests: diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 6a8d42a5090..5daeb97fa0f 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -89,6 +89,9 @@ msgstr "" msgid "\"el\" parameter is required for createInstance()" msgstr "" +msgid "#general, #development" +msgstr "" + msgid "%d Approval" msgid_plural "%d Approvals" msgstr[0] "" @@ -38678,9 +38681,6 @@ msgid_plural "from %d jobs" msgstr[0] "" msgstr[1] "" -msgid "general, development" -msgstr "" - msgid "group" msgstr "" diff --git a/package.json b/package.json index f5e8e4d8431..50fdeb60019 100644 --- a/package.json +++ b/package.json @@ -129,7 +129,7 @@ "immer": "^7.0.7", "ipaddr.js": "^1.9.1", "jed": "^1.1.1", - "jquery": "^3.5.0", + "jquery": "^3.6.0", "jquery.caret": "^0.3.1", "jquery.waitforimages": "^2.2.0", "js-cookie": "^2.2.1", diff --git a/scripts/frontend/startup_css/constants.js b/scripts/frontend/startup_css/constants.js index a76092f8b45..5f6189d9e59 100644 --- a/scripts/frontend/startup_css/constants.js +++ b/scripts/frontend/startup_css/constants.js @@ -10,6 +10,8 @@ const HTML_TO_REMOVE = [ '#js-peek', '.modal', '.feature-highlight', + // The user has to open up the responsive nav, so we don't need it on load + '.top-nav-responsive', // We don't want to capture all the children of a dropdown-menu '.dropdown-menu', ]; diff --git a/spec/features/admin/admin_mode_spec.rb b/spec/features/admin/admin_mode_spec.rb index dfb6ebc0211..37d77beb2bb 100644 --- a/spec/features/admin/admin_mode_spec.rb +++ b/spec/features/admin/admin_mode_spec.rb @@ -97,6 +97,8 @@ RSpec.describe 'Admin mode' do end it 'can leave admin mode using dropdown menu on smaller screens', :js do + skip('pending responsive development under :combined_menu feature flag') if Feature.enabled?(:combined_menu) + resize_screen_xs visit root_dashboard_path @@ -131,7 +133,7 @@ RSpec.describe 'Admin mode' do end it 'relocates admin dashboard links to dropdown list on smaller screen', :js do - skip('not applicable with :combined_menu feature flag enabled') if Feature.enabled?(:combined_menu) + skip('pending responsive development under :combined_menu feature flag') if Feature.enabled?(:combined_menu) resize_screen_xs visit root_dashboard_path diff --git a/spec/features/nav/top_nav_responsive_spec.rb b/spec/features/nav/top_nav_responsive_spec.rb new file mode 100644 index 00000000000..0d8c75a099d --- /dev/null +++ b/spec/features/nav/top_nav_responsive_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe 'top nav responsive', :js do + include MobileHelpers + + let_it_be(:user) { create(:user) } + let_it_be(:responsive_menu_text) { 'Placeholder for responsive top nav' } + + before do + stub_feature_flags(combined_menu: true) + + sign_in(user) + visit explore_projects_path + + resize_screen_xs + end + + context 'before opened' do + it 'has page content and hides responsive menu', :aggregate_failures do + expect(page).to have_css('.page-title', text: 'Projects') + expect(page).to have_no_text(responsive_menu_text) + end + end + + context 'when opened' do + before do + click_button('Menu') + end + + it 'hides everything and shows responsive menu', :aggregate_failures do + expect(page).to have_no_css('.page-title', text: 'Projects') + expect(page).to have_link('Dashboard', id: 'logo') + expect(page).to have_text(responsive_menu_text) + end + end +end diff --git a/spec/frontend/integrations/edit/components/trigger_fields_spec.js b/spec/frontend/integrations/edit/components/trigger_fields_spec.js index 5f85c58da28..a0816682741 100644 --- a/spec/frontend/integrations/edit/components/trigger_fields_spec.js +++ b/spec/frontend/integrations/edit/components/trigger_fields_spec.js @@ -137,11 +137,11 @@ describe('TriggerFields', () => { const expectedResults = [ { name: 'service[push_channel]', - placeholder: 'general, development', + placeholder: '#general, #development', }, { name: 'service[merge_request_channel]', - placeholder: 'general, development', + placeholder: '#general, #development', }, ]; diff --git a/spec/frontend/nav/components/responsive_app_spec.js b/spec/frontend/nav/components/responsive_app_spec.js new file mode 100644 index 00000000000..1a153b38a05 --- /dev/null +++ b/spec/frontend/nav/components/responsive_app_spec.js @@ -0,0 +1,62 @@ +import { shallowMount } from '@vue/test-utils'; +import { range } from 'lodash'; +import ResponsiveApp from '~/nav/components/responsive_app.vue'; +import eventHub, { EVENT_RESPONSIVE_TOGGLE } from '~/nav/event_hub'; +import { TEST_NAV_DATA } from '../mock_data'; + +describe('~/nav/components/responsive_app.vue', () => { + let wrapper; + + const createComponent = () => { + wrapper = shallowMount(ResponsiveApp, { + propsData: { + navData: TEST_NAV_DATA, + }, + }); + }; + const triggerResponsiveToggle = () => eventHub.$emit(EVENT_RESPONSIVE_TOGGLE); + + const hasBodyResponsiveOpen = () => document.body.classList.contains('top-nav-responsive-open'); + + beforeEach(() => { + // Add test class to reset state + assert that we're adding classes correctly + document.body.className = 'test-class'; + }); + + afterEach(() => { + wrapper.destroy(); + }); + + describe('default', () => { + beforeEach(() => { + createComponent(); + }); + + it.each` + times | expectation + ${0} | ${false} + ${1} | ${true} + ${2} | ${false} + `( + 'with responsive toggle event triggered $times, body responsive open = $expectation', + ({ times, expectation }) => { + range(times).forEach(triggerResponsiveToggle); + + expect(hasBodyResponsiveOpen()).toBe(expectation); + }, + ); + }); + + describe('when destroyed', () => { + beforeEach(() => { + createComponent(); + wrapper.destroy(); + }); + + it('responsive toggle event does nothing', () => { + triggerResponsiveToggle(); + + expect(hasBodyResponsiveOpen()).toBe(false); + }); + }); +}); diff --git a/spec/migrations/backfill_draft_status_on_merge_requests_spec.rb b/spec/migrations/backfill_draft_status_on_merge_requests_spec.rb deleted file mode 100644 index 2015f947f15..00000000000 --- a/spec/migrations/backfill_draft_status_on_merge_requests_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' -require Rails.root.join('db', 'post_migrate', '20210526222715_backfill_draft_status_on_merge_requests.rb') - -RSpec.describe BackfillDraftStatusOnMergeRequests, :migration do - let(:namespaces) { table(:namespaces) } - let(:projects) { table(:projects) } - let(:merge_requests) { table(:merge_requests) } - - let(:group) { namespaces.create!(name: 'gitlab', path: 'gitlab') } - let(:project) { projects.create!(namespace_id: group.id) } - - let(:draft_prefixes) { ["[Draft]", "(Draft)", "Draft:", "Draft", "[WIP]", "WIP:", "WIP"] } - - def create_merge_request(params) - common_params = { - target_project_id: project.id, - target_branch: 'feature1', - source_branch: 'master' - } - - merge_requests.create!(common_params.merge(params)) - end - - context "for MRs with #draft? == true titles but draft attribute false" do - before do - draft_prefixes.each do |prefix| - (1..4).each do |n| - merge_request = create_merge_request(title: "#{prefix} This is a title", state_id: n) - merge_request.update_columns(draft: false) - end - end - end - - it "updates all open draft merge request's draft field to true" do - mr_count = merge_requests.all.count - - expect { disable_migrations_output { migrate! } } - .to change { MergeRequest.where(draft: false).count } - .from(mr_count).to(mr_count - draft_prefixes.length) - end - end -end diff --git a/yarn.lock b/yarn.lock index 8c549491e58..4dfe1a5fd5c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7336,10 +7336,10 @@ jquery.waitforimages@^2.2.0: resolved "https://registry.yarnpkg.com/jquery.waitforimages/-/jquery.waitforimages-2.2.0.tgz#63f23131055a1b060dc913e6d874bcc9b9e6b16b" integrity sha1-Y/IxMQVaGwYNyRPm2HS8ybnmsWs= -"jquery@>= 1.9.1", jquery@^3.5.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.5.1.tgz#d7b4d08e1bfdb86ad2f1a3d039ea17304717abb5" - integrity sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg== +"jquery@>= 1.9.1", jquery@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.0.tgz#c72a09f15c1bdce142f49dbf1170bdf8adac2470" + integrity sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw== js-beautify@^1.6.12, js-beautify@^1.8.8: version "1.11.0"