From 4e06ca9e7d67aee59d691a363dabba46d53d34ba Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 2 Sep 2020 18:10:40 +0000 Subject: [PATCH] Add latest changes from gitlab-org/gitlab@master --- .rubocop_todo.yml | 21 - GITALY_SERVER_VERSION | 2 +- Gemfile | 4 +- Gemfile.lock | 12 +- app/assets/javascripts/boards/boards_util.js | 29 +- .../javascripts/boards/stores/getters.js | 7 + .../javascripts/boards/stores/mutations.js | 5 +- app/assets/javascripts/boards/stores/state.js | 1 + .../javascripts/deploy_keys/service/index.js | 10 +- .../issue_show/utils/parse_data.js | 7 +- app/assets/stylesheets/framework.scss | 1 - .../stylesheets/framework/gitlab_theme.scss | 431 ------------------ .../stylesheets/startup/startup-general.scss | 79 ---- app/assets/stylesheets/themes/theme_blue.scss | 14 + app/assets/stylesheets/themes/theme_dark.scss | 14 + .../stylesheets/themes/theme_green.scss | 14 + .../stylesheets/themes/theme_helper.scss | 204 +++++++++ .../stylesheets/themes/theme_indigo.scss | 14 + .../stylesheets/themes/theme_light.scss | 129 ++++++ .../stylesheets/themes/theme_light_blue.scss | 14 + .../stylesheets/themes/theme_light_green.scss | 14 + .../themes/theme_light_indigo.scss | 14 + .../stylesheets/themes/theme_light_red.scss | 14 + app/assets/stylesheets/themes/theme_red.scss | 14 + .../oauth/jira/authorizations_controller.rb | 1 + app/helpers/preferences_helper.rb | 4 + .../concerns/enums/prometheus_metric.rb | 9 +- .../concerns/mentionable/reference_regexes.rb | 2 +- app/models/prometheus_metric.rb | 2 + app/services/ci/process_pipeline_service.rb | 10 +- app/services/projects/download_service.rb | 2 +- app/views/admin/applications/_form.html.haml | 5 - .../doorkeeper/applications/_form.html.haml | 3 - app/views/layouts/_head.html.haml | 2 + app/views/layouts/_startup_css.haml | 1 + app/views/profiles/preferences/show.html.haml | 3 + app/views/shared/notes/_hints.html.haml | 2 +- ...t-null-on-mr-metrics-target-project-id.yml | 5 + ...-hosted-smtp-configurations-by-package.yml | 5 + ...edit-file-upload-button-tab-accessible.yml | 5 + changelogs/unreleased/commented-keyword.yml | 5 + .../unreleased/id-bump-doorkeeper-5-3.yml | 5 + changelogs/unreleased/lint-uri-regex.yml | 5 + .../unreleased/multiline-if-modifier.yml | 5 + config/application.rb | 2 + config/initializers/doorkeeper.rb | 7 - config/locales/doorkeeper.en.yml | 7 +- ...valid_not_null_constraint_to_mr_metrics.rb | 17 + ...5322_add_tmp_index_to_target_project_id.rb | 18 + ...5705_ensure_target_project_id_is_filled.rb | 49 ++ ...idate_not_null_constraint_on_mr_metrics.rb | 20 + db/schema_migrations/20200831065320 | 1 + db/schema_migrations/20200831065322 | 1 + db/schema_migrations/20200831065705 | 1 + db/schema_migrations/20200831074356 | 1 + db/structure.sql | 3 +- doc/administration/feature_flags.md | 6 +- doc/development/documentation/styleguide.md | 19 +- doc/development/feature_flags/index.md | 46 +- ...unning_tests_that_require_special_setup.md | 125 +++++ doc/integration/elasticsearch.md | 9 +- doc/operations/feature_flags.md | 4 + .../{alertdetails.md => alert_details.md} | 22 +- doc/operations/incident_management/alerts.md | 12 +- .../incident_management/generic_alerts.md | 4 +- .../incident_management/incidents.md | 8 +- doc/operations/incident_management/index.md | 25 +- .../incident_management/status_page.md | 6 +- .../dependency_scanning/index.md | 3 +- doc/user/feature_flags.md | 7 +- .../repository/repository_mirroring.md | 5 + lib/api/applications.rb | 16 + lib/api/commit_statuses.rb | 6 +- .../pipeline/chain/config/content/remote.rb | 2 +- .../common_metrics/prometheus_metric_enums.rb | 4 +- lib/gitlab/metrics/dashboard/importer.rb | 41 ++ .../dashboard/importers/prometheus_metrics.rb | 72 +++ .../metrics/dashboard/transformers/errors.rb | 19 + .../transformers/yml/v1/prometheus_metrics.rb | 54 +++ lib/gitlab/usage_data.rb | 3 + lib/mattermost/session.rb | 2 +- lib/tasks/gitlab/backup.rake | 6 +- locale/gitlab.pot | 6 +- package.json | 2 +- qa/Dockerfile | 4 +- rubocop/rubocop-usage-data.yml | 1 + scripts/review_apps/review-apps.sh | 5 +- spec/frontend/boards/stores/getters_spec.js | 24 + spec/frontend/boards/stores/mutations_spec.js | 9 +- .../issue_show/components/app_spec.js | 35 +- spec/frontend/issue_show/issue_spec.js | 45 +- spec/frontend/issue_show/mock_data.js | 25 + .../gitlab/metrics/dashboard/importer_spec.rb | 55 +++ .../importers/prometheus_metrics_spec.rb | 79 ++++ .../yml/v1/prometheus_metrics_spec.rb | 99 ++++ spec/lib/gitlab/usage_data_spec.rb | 1 + ...ensure_target_project_id_is_filled_spec.rb | 30 ++ spec/models/merge_request/metrics_spec.rb | 11 - spec/models/merge_request_spec.rb | 12 - spec/tasks/gitlab/backup_rake_spec.rb | 9 +- yarn.lock | 20 +- 101 files changed, 1539 insertions(+), 735 deletions(-) delete mode 100644 app/assets/stylesheets/framework/gitlab_theme.scss create mode 100644 app/assets/stylesheets/themes/theme_blue.scss create mode 100644 app/assets/stylesheets/themes/theme_dark.scss create mode 100644 app/assets/stylesheets/themes/theme_green.scss create mode 100644 app/assets/stylesheets/themes/theme_helper.scss create mode 100644 app/assets/stylesheets/themes/theme_indigo.scss create mode 100644 app/assets/stylesheets/themes/theme_light.scss create mode 100644 app/assets/stylesheets/themes/theme_light_blue.scss create mode 100644 app/assets/stylesheets/themes/theme_light_green.scss create mode 100644 app/assets/stylesheets/themes/theme_light_indigo.scss create mode 100644 app/assets/stylesheets/themes/theme_light_red.scss create mode 100644 app/assets/stylesheets/themes/theme_red.scss create mode 100644 changelogs/unreleased/233507-set-not-null-on-mr-metrics-target-project-id.yml create mode 100644 changelogs/unreleased/238111-count-of-the-number-of-self-hosted-smtp-configurations-by-package.yml create mode 100644 changelogs/unreleased/cngo-make-mr-edit-file-upload-button-tab-accessible.yml create mode 100644 changelogs/unreleased/commented-keyword.yml create mode 100644 changelogs/unreleased/id-bump-doorkeeper-5-3.yml create mode 100644 changelogs/unreleased/lint-uri-regex.yml create mode 100644 changelogs/unreleased/multiline-if-modifier.yml create mode 100644 db/post_migrate/20200831065320_add_not_valid_not_null_constraint_to_mr_metrics.rb create mode 100644 db/post_migrate/20200831065322_add_tmp_index_to_target_project_id.rb create mode 100644 db/post_migrate/20200831065705_ensure_target_project_id_is_filled.rb create mode 100644 db/post_migrate/20200831074356_validate_not_null_constraint_on_mr_metrics.rb create mode 100644 db/schema_migrations/20200831065320 create mode 100644 db/schema_migrations/20200831065322 create mode 100644 db/schema_migrations/20200831065705 create mode 100644 db/schema_migrations/20200831074356 rename doc/operations/incident_management/{alertdetails.md => alert_details.md} (90%) create mode 100644 lib/gitlab/metrics/dashboard/importer.rb create mode 100644 lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb create mode 100644 lib/gitlab/metrics/dashboard/transformers/errors.rb create mode 100644 lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb create mode 100644 spec/lib/gitlab/metrics/dashboard/importer_spec.rb create mode 100644 spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb create mode 100644 spec/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics_spec.rb create mode 100644 spec/migrations/ensure_target_project_id_is_filled_spec.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 6c46ad5dffc..82e59e0104b 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -113,14 +113,6 @@ Lint/UriEscapeUnescape: - 'spec/lib/google_api/auth_spec.rb' - 'spec/requests/api/files_spec.rb' -# Offense count: 3 -# Cop supports --auto-correct. -Lint/UriRegexp: - Exclude: - - 'app/models/concerns/mentionable/reference_regexes.rb' - - 'app/services/projects/download_service.rb' - - 'lib/gitlab/ci/pipeline/chain/config/content/remote.rb' - # Offense count: 65 # Cop supports --auto-correct. Migration/DepartmentName: @@ -539,12 +531,6 @@ Style/AccessModifierDeclarations: Style/BarePercentLiterals: Enabled: false -# Offense count: 5 -Style/CommentedKeyword: - Exclude: - - 'lib/tasks/gitlab/backup.rake' - - 'spec/tasks/gitlab/backup_rake_spec.rb' - # Offense count: 5 # Cop supports --auto-correct. Style/EachWithObject: @@ -627,13 +613,6 @@ Style/MixinUsage: - 'spec/factories/notes.rb' - 'spec/lib/gitlab/import_export/version_checker_spec.rb' -# Offense count: 2 -# Cop supports --auto-correct. -Style/MultilineIfModifier: - Exclude: - - 'app/services/ci/process_pipeline_service.rb' - - 'lib/api/commit_statuses.rb' - # Offense count: 29 # Cop supports --auto-correct. # Configuration parameters: EnforcedStyle, MinBodyLength. diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index acaadbc3902..93471b43e4b 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -12d115c50517935dc8e7e2e1248aa450bf00710e +10ef65465876db3a9539dba1aa4d789f1cf2cb3b diff --git a/Gemfile b/Gemfile index 2495a5ca48a..89cb29d3034 100644 --- a/Gemfile +++ b/Gemfile @@ -26,8 +26,8 @@ gem 'marginalia', '~> 1.9.0' # Authentication libraries gem 'devise', '~> 4.6' -gem 'doorkeeper', '~> 5.1.1' -gem 'doorkeeper-openid_connect', '~> 1.6.3' +gem 'doorkeeper', '~> 5.3.0' +gem 'doorkeeper-openid_connect', '~> 1.7.4' gem 'omniauth', '~> 1.8' gem 'omniauth-auth0', '~> 2.0.0' gem 'omniauth-azure-oauth2', '~> 0.0.9' diff --git a/Gemfile.lock b/Gemfile.lock index 53ede64312b..b6daa9abaa7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -254,11 +254,11 @@ GEM docile (1.3.2) domain_name (0.5.20180417) unf (>= 0.0.5, < 1.0.0) - doorkeeper (5.1.1) + doorkeeper (5.3.3) railties (>= 5) - doorkeeper-openid_connect (1.6.3) - doorkeeper (>= 5.0, < 5.2) - json-jwt (~> 1.6) + doorkeeper-openid_connect (1.7.4) + doorkeeper (>= 5.2, < 5.5) + json-jwt (>= 1.11.0) dry-configurable (0.11.5) concurrent-ruby (~> 1.0) dry-core (~> 0.4, >= 0.4.7) @@ -1265,8 +1265,8 @@ DEPENDENCIES diff_match_patch (~> 0.1.0) diffy (~> 3.3) discordrb-webhooks-blackst0ne (~> 3.3) - doorkeeper (~> 5.1.1) - doorkeeper-openid_connect (~> 1.6.3) + doorkeeper (~> 5.3.0) + doorkeeper-openid_connect (~> 1.7.4) ed25519 (~> 1.2) elasticsearch-api (~> 6.8) elasticsearch-model (~> 6.1) diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js index a83ffdcb7ff..652f8a8a107 100644 --- a/app/assets/javascripts/boards/boards_util.js +++ b/app/assets/javascripts/boards/boards_util.js @@ -6,20 +6,29 @@ export function getMilestone() { } export function formatListIssues(listIssues) { - return listIssues.nodes.reduce((map, list) => { + const issues = {}; + + const listData = listIssues.nodes.reduce((map, list) => { return { ...map, - [list.id]: list.issues.nodes.map( - i => - new ListIssue({ - ...i, - id: getIdFromGraphQLId(i.id), - labels: i.labels?.nodes || [], - assignees: i.assignees?.nodes || [], - }), - ), + [list.id]: list.issues.nodes.map(i => { + const id = getIdFromGraphQLId(i.id); + + const listIssue = new ListIssue({ + ...i, + id, + labels: i.labels?.nodes || [], + assignees: i.assignees?.nodes || [], + }); + + issues[id] = listIssue; + + return id; + }), }; }, {}); + + return { listData, issues }; } export function fullBoardId(boardId) { diff --git a/app/assets/javascripts/boards/stores/getters.js b/app/assets/javascripts/boards/stores/getters.js index a6ac64f432a..80acf8e66cc 100644 --- a/app/assets/javascripts/boards/stores/getters.js +++ b/app/assets/javascripts/boards/stores/getters.js @@ -10,4 +10,11 @@ export default { return state.isShowingEpicsSwimlanes; }, + getIssueById: state => id => { + return state.issues[id] || {}; + }, + + getActiveIssue: state => { + return state.issues[state.activeId] || {}; + }, }; diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js index 16071ed8c72..33bb8c1ddb3 100644 --- a/app/assets/javascripts/boards/stores/mutations.js +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -72,8 +72,9 @@ export default { state.isLoadingIssues = true; }, - [mutationTypes.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS]: (state, listIssues) => { - state.issuesByListId = listIssues; + [mutationTypes.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS]: (state, { listData, issues }) => { + state.issuesByListId = listData; + state.issues = issues; state.isLoadingIssues = false; }, diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js index a4ff637f730..be937d68c6c 100644 --- a/app/assets/javascripts/boards/stores/state.js +++ b/app/assets/javascripts/boards/stores/state.js @@ -10,6 +10,7 @@ export default () => ({ sidebarType: '', boardLists: [], issuesByListId: {}, + issues: {}, isLoadingIssues: false, filterParams: {}, error: undefined, diff --git a/app/assets/javascripts/deploy_keys/service/index.js b/app/assets/javascripts/deploy_keys/service/index.js index 268a37008c5..10333752936 100644 --- a/app/assets/javascripts/deploy_keys/service/index.js +++ b/app/assets/javascripts/deploy_keys/service/index.js @@ -2,20 +2,18 @@ import axios from '~/lib/utils/axios_utils'; export default class DeployKeysService { constructor(endpoint) { - this.axios = axios.create({ - baseURL: endpoint, - }); + this.endpoint = endpoint; } getKeys() { - return this.axios.get().then(response => response.data); + return axios.get(this.endpoint).then(response => response.data); } enableKey(id) { - return this.axios.put(`${id}/enable`).then(response => response.data); + return axios.put(`${this.endpoint}/${id}/enable`).then(response => response.data); } disableKey(id) { - return this.axios.put(`${id}/disable`).then(response => response.data); + return axios.put(`${this.endpoint}/${id}/disable`).then(response => response.data); } } diff --git a/app/assets/javascripts/issue_show/utils/parse_data.js b/app/assets/javascripts/issue_show/utils/parse_data.js index 8cd1c1b0e56..52747ceab80 100644 --- a/app/assets/javascripts/issue_show/utils/parse_data.js +++ b/app/assets/javascripts/issue_show/utils/parse_data.js @@ -4,7 +4,12 @@ export const parseIssuableData = () => { try { const initialDataEl = document.getElementById('js-issuable-app-initial-data'); - return JSON.parse(sanitize(initialDataEl.textContent).replace(/"/g, '"')); + const parsedData = JSON.parse(initialDataEl.textContent.replace(/"/g, '"')); + + parsedData.initialTitleHtml = sanitize(parsedData.initialTitleHtml); + parsedData.initialDescriptionHtml = sanitize(parsedData.initialDescriptionHtml); + + return parsedData; } catch (e) { console.error(e); // eslint-disable-line no-console diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss index 413e0dde535..f875420b9c9 100644 --- a/app/assets/stylesheets/framework.scss +++ b/app/assets/stylesheets/framework.scss @@ -23,7 +23,6 @@ @import 'framework/flash'; @import 'framework/forms'; @import 'framework/gfm'; -@import 'framework/gitlab_theme'; @import 'framework/header'; @import 'framework/highlight'; @import 'framework/issue_box'; diff --git a/app/assets/stylesheets/framework/gitlab_theme.scss b/app/assets/stylesheets/framework/gitlab_theme.scss deleted file mode 100644 index 97bd6ca6fe2..00000000000 --- a/app/assets/stylesheets/framework/gitlab_theme.scss +++ /dev/null @@ -1,431 +0,0 @@ -/** - * Styles the GitLab application with a specific color theme - */ - -@mixin gitlab-theme( - $search-and-nav-links, - $active-tab-border, - $border-and-box-shadow, - $sidebar-text, - $nav-svg-color, - $color-alternate -) { - // Header - - .navbar-gitlab { - background-color: $nav-svg-color; - - .navbar-collapse { - color: $search-and-nav-links; - } - - .container-fluid { - .navbar-toggler { - border-left: 1px solid lighten($border-and-box-shadow, 10%); - - svg { - fill: $search-and-nav-links; - } - } - } - - .navbar-sub-nav, - .navbar-nav { - > li { - > a, - > button { - &:hover, - &:focus { - background-color: rgba($search-and-nav-links, 0.2); - } - } - - &.active, - &.dropdown.show { - > a, - > button { - color: $nav-svg-color; - background-color: $color-alternate; - } - } - - &.line-separator { - border-left: 1px solid rgba($search-and-nav-links, 0.2); - } - } - } - - .navbar-sub-nav { - color: $search-and-nav-links; - } - - .nav { - > li { - color: $search-and-nav-links; - - > a { - &.header-user-dropdown-toggle { - .header-user-avatar { - border-color: $search-and-nav-links; - } - - .header-user-notification-dot { - border: 2px solid $nav-svg-color; - } - } - - &:hover, - &:focus { - @include media-breakpoint-up(sm) { - background-color: rgba($search-and-nav-links, 0.2); - } - - svg { - fill: currentColor; - } - - &.header-user-dropdown-toggle .header-user-notification-dot { - border-color: $nav-svg-color + 33; - } - } - } - - &.active > a, - &.dropdown.show > a { - color: $nav-svg-color; - background-color: $color-alternate; - - &:hover { - svg { - fill: $nav-svg-color; - } - } - - &.header-user-dropdown-toggle .header-user-notification-dot { - border-color: $white; - } - } - - .impersonated-user, - .impersonated-user:hover { - svg { - fill: $nav-svg-color; - } - } - } - } - } - - .navbar .title { - > a { - &:hover, - &:focus { - background-color: rgba($search-and-nav-links, 0.2); - } - } - } - - .search { - form { - background-color: rgba($search-and-nav-links, 0.2); - - &:hover { - background-color: rgba($search-and-nav-links, 0.3); - } - } - - .search-input::placeholder { - color: rgba($search-and-nav-links, 0.8); - } - - .search-input-wrap { - .search-icon, - .clear-icon { - fill: rgba($search-and-nav-links, 0.8); - } - } - - &.search-active { - form { - background-color: $white; - } - - .search-input-wrap { - .search-icon { - fill: rgba($search-and-nav-links, 0.8); - } - } - } - } - - // Sidebar - .nav-sidebar li.active { - box-shadow: inset 4px 0 0 $border-and-box-shadow; - - > a { - color: $sidebar-text; - } - - .nav-icon-container svg { - fill: $sidebar-text; - } - } - - .sidebar-top-level-items > li.active .badge.badge-pill { - color: $sidebar-text; - } - - .nav-links li { - &.active a, - &.md-header-tab.active button, - a.active { - border-bottom: 2px solid $active-tab-border; - - .badge.badge-pill { - font-weight: $gl-font-weight-bold; - } - } - } - - .branch-header-title { - color: $border-and-box-shadow; - } - - .ide-sidebar-link { - &.active { - color: $border-and-box-shadow; - box-shadow: inset 3px 0 $border-and-box-shadow; - - &.is-right { - box-shadow: inset -3px 0 $border-and-box-shadow; - } - } - } -} - -body { - &.ui-indigo { - @include gitlab-theme( - $indigo-200, - $indigo-500, - $indigo-700, - $indigo-800, - $indigo-900, - $white - ); - } - - &.ui-light-indigo { - @include gitlab-theme( - $indigo-200, - $indigo-500, - $indigo-500, - $indigo-700, - $indigo-700, - $white - ); - } - - &.ui-blue { - @include gitlab-theme( - $theme-blue-200, - $theme-blue-500, - $theme-blue-700, - $theme-blue-800, - $theme-blue-900, - $white - ); - } - - &.ui-light-blue { - @include gitlab-theme( - $theme-light-blue-200, - $theme-light-blue-500, - $theme-light-blue-500, - $theme-light-blue-700, - $theme-light-blue-700, - $white - ); - } - - &.ui-green { - @include gitlab-theme( - $theme-green-200, - $theme-green-500, - $theme-green-700, - $theme-green-800, - $theme-green-900, - $white - ); - } - - &.ui-light-green { - @include gitlab-theme( - $theme-green-200, - $theme-green-500, - $theme-green-500, - $theme-light-green-700, - $theme-light-green-700, - $white - ); - } - - &.ui-red { - @include gitlab-theme( - $theme-red-200, - $theme-red-500, - $theme-red-700, - $theme-red-800, - $theme-red-900, - $white - ); - } - - &.ui-light-red { - @include gitlab-theme( - $theme-light-red-200, - $theme-light-red-500, - $theme-light-red-500, - $theme-light-red-700, - $theme-light-red-700, - $white - ); - } - - &.ui-dark { - @include gitlab-theme( - $gray-200, - $gray-300, - $gray-500, - $gray-700, - $gray-900, - $white - ); - } - - &.ui-light { - @include gitlab-theme( - $gray-500, - $gray-700, - $gray-500, - $gray-500, - $gray-50, - $gray-500 - ); - - .navbar-gitlab { - background-color: $gray-50; - box-shadow: 0 1px 0 0 $border-color; - - .logo-text svg { - fill: $gray-900; - } - - .navbar-sub-nav, - .navbar-nav { - > li { - > a:hover, - > a:focus, - > button:hover { - color: $gray-900; - } - - &.active > a, - &.active > a:hover, - &.active > button { - color: $white; - } - } - } - - .container-fluid { - .navbar-toggler, - .navbar-toggler:hover { - color: $gray-500; - border-left: 1px solid $gray-100; - } - } - } - - .search { - form { - background-color: $white; - box-shadow: inset 0 0 0 1px $border-color; - - &:hover { - background-color: $white; - box-shadow: inset 0 0 0 1px $blue-200; - } - } - - .search-input-wrap { - .search-icon { - fill: $gray-100; - } - - .search-input { - color: $gl-text-color; - } - } - } - - .nav-sidebar li.active { - > a { - color: $gray-900; - } - - svg { - fill: $gray-900; - } - } - - .sidebar-top-level-items > li.active .badge.badge-pill { - color: $gray-900; - } - } - - &.gl-dark { - .logo-text svg { - fill: $gl-text-color; - } - - .navbar-gitlab { - background-color: $gray-50; - - .navbar-sub-nav, - .navbar-nav { - li { - > a:hover, - > a:focus, - > button:hover, - > button:focus { - color: $gl-text-color; - background-color: $gray-200; - } - } - - li.active, - li.dropdown.show { - > a, - > button { - color: $gl-text-color; - background-color: $gray-200; - } - } - } - - .search { - form { - background-color: $gray-100; - box-shadow: inset 0 0 0 1px $border-color; - - &:active, - &:hover { - background-color: $gray-100; - box-shadow: inset 0 0 0 1px $blue-200; - } - } - } - } - } -} diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss index c0b4c5e0bcd..a98e91b32eb 100644 --- a/app/assets/stylesheets/startup/startup-general.scss +++ b/app/assets/stylesheets/startup/startup-general.scss @@ -968,85 +968,6 @@ input { .search form::placeholder { color: #868686; } -body.ui-indigo .navbar-gitlab { - background-color: #292961; -} -body.ui-indigo .navbar-gitlab .navbar-collapse { - color: #d1d1f0; -} -body.ui-indigo .navbar-gitlab .container-fluid .navbar-toggler { - border-left: 1px solid #6868b9; -} -body.ui-indigo .navbar-gitlab .container-fluid .navbar-toggler svg { - fill: #d1d1f0; -} -body.ui-indigo .navbar-gitlab .navbar-sub-nav > li.active > a, -body.ui-indigo .navbar-gitlab .navbar-sub-nav > li.active > button, body.ui-indigo .navbar-gitlab .navbar-sub-nav > li.dropdown.show > a, -body.ui-indigo .navbar-gitlab .navbar-sub-nav > li.dropdown.show > button, -body.ui-indigo .navbar-gitlab .navbar-nav > li.active > a, -body.ui-indigo .navbar-gitlab .navbar-nav > li.active > button, -body.ui-indigo .navbar-gitlab .navbar-nav > li.dropdown.show > a, -body.ui-indigo .navbar-gitlab .navbar-nav > li.dropdown.show > button { - color: #292961; - background-color: #fff; -} -body.ui-indigo .navbar-gitlab .navbar-sub-nav { - color: #d1d1f0; -} -body.ui-indigo .navbar-gitlab .nav > li { - color: #d1d1f0; -} -body.ui-indigo .navbar-gitlab .nav > li > a.header-user-dropdown-toggle .header-user-avatar { - border-color: #d1d1f0; -} -body.ui-indigo .navbar-gitlab .nav > li.active > a, -body.ui-indigo .navbar-gitlab .nav > li.dropdown.show > a { - color: #292961; - background-color: #fff; -} -body.ui-indigo .search form { - background-color: rgba(209, 209, 240, 0.2); -} -body.ui-indigo .search .search-input::placeholder { - color: rgba(209, 209, 240, 0.8); -} -body.ui-indigo .search .search-input-wrap .search-icon, -body.ui-indigo .search .search-input-wrap .clear-icon { - fill: rgba(209, 209, 240, 0.8); -} -body.ui-indigo .nav-sidebar li.active { - box-shadow: inset 4px 0 0 #4b4ba3; -} -body.ui-indigo .nav-sidebar li.active > a { - color: #393982; -} -body.ui-indigo .nav-sidebar li.active .nav-icon-container svg { - fill: #393982; -} -body.ui-indigo .sidebar-top-level-items > li.active .badge.badge-pill { - color: #393982; -} -body.gl-dark .logo-text svg { - fill: #303030; -} -body.gl-dark .navbar-gitlab { - background-color: #f0f0f0; -} -body.gl-dark .navbar-gitlab .navbar-sub-nav li.active > a, -body.gl-dark .navbar-gitlab .navbar-sub-nav li.active > button, -body.gl-dark .navbar-gitlab .navbar-sub-nav li.dropdown.show > a, -body.gl-dark .navbar-gitlab .navbar-sub-nav li.dropdown.show > button, -body.gl-dark .navbar-gitlab .navbar-nav li.active > a, -body.gl-dark .navbar-gitlab .navbar-nav li.active > button, -body.gl-dark .navbar-gitlab .navbar-nav li.dropdown.show > a, -body.gl-dark .navbar-gitlab .navbar-nav li.dropdown.show > button { - color: #303030; - background-color: #bfbfbf; -} -body.gl-dark .navbar-gitlab .search form { - background-color: #dbdbdb; - box-shadow: inset 0 0 0 1px #dbdbdb; -} .navbar-gitlab { padding: 0 16px; z-index: 1000; diff --git a/app/assets/stylesheets/themes/theme_blue.scss b/app/assets/stylesheets/themes/theme_blue.scss new file mode 100644 index 00000000000..9f9802f77f4 --- /dev/null +++ b/app/assets/stylesheets/themes/theme_blue.scss @@ -0,0 +1,14 @@ +@import './theme_helper'; + +body { + &.ui-blue { + @include gitlab-theme( + $theme-blue-200, + $theme-blue-500, + $theme-blue-700, + $theme-blue-800, + $theme-blue-900, + $white + ); + } +} diff --git a/app/assets/stylesheets/themes/theme_dark.scss b/app/assets/stylesheets/themes/theme_dark.scss new file mode 100644 index 00000000000..e6db6cd2a5e --- /dev/null +++ b/app/assets/stylesheets/themes/theme_dark.scss @@ -0,0 +1,14 @@ +@import './theme_helper'; + +body { + &.ui-dark { + @include gitlab-theme( + $gray-200, + $gray-300, + $gray-500, + $gray-700, + $gray-900, + $white + ); + } +} diff --git a/app/assets/stylesheets/themes/theme_green.scss b/app/assets/stylesheets/themes/theme_green.scss new file mode 100644 index 00000000000..6dcad6e1301 --- /dev/null +++ b/app/assets/stylesheets/themes/theme_green.scss @@ -0,0 +1,14 @@ +@import './theme_helper'; + +body { + &.ui-green { + @include gitlab-theme( + $theme-green-200, + $theme-green-500, + $theme-green-700, + $theme-green-800, + $theme-green-900, + $white + ); + } +} diff --git a/app/assets/stylesheets/themes/theme_helper.scss b/app/assets/stylesheets/themes/theme_helper.scss new file mode 100644 index 00000000000..85115cfd5d9 --- /dev/null +++ b/app/assets/stylesheets/themes/theme_helper.scss @@ -0,0 +1,204 @@ +@import '../page_bundles/mixins_and_variables_and_functions'; +/** + * Styles the GitLab application with a specific color theme + */ +@mixin gitlab-theme( + $search-and-nav-links, + $active-tab-border, + $border-and-box-shadow, + $sidebar-text, + $nav-svg-color, + $color-alternate +) { + // Header + + .navbar-gitlab { + background-color: $nav-svg-color; + + .navbar-collapse { + color: $search-and-nav-links; + } + + .container-fluid { + .navbar-toggler { + border-left: 1px solid lighten($border-and-box-shadow, 10%); + + svg { + fill: $search-and-nav-links; + } + } + } + + .navbar-sub-nav, + .navbar-nav { + > li { + > a, + > button { + &:hover, + &:focus { + background-color: rgba($search-and-nav-links, 0.2); + } + } + + &.active, + &.dropdown.show { + > a, + > button { + color: $nav-svg-color; + background-color: $color-alternate; + } + } + + &.line-separator { + border-left: 1px solid rgba($search-and-nav-links, 0.2); + } + } + } + + .navbar-sub-nav { + color: $search-and-nav-links; + } + + .nav { + > li { + color: $search-and-nav-links; + + > a { + &.header-user-dropdown-toggle { + .header-user-avatar { + border-color: $search-and-nav-links; + } + + .header-user-notification-dot { + border: 2px solid $nav-svg-color; + } + } + + &:hover, + &:focus { + @include media-breakpoint-up(sm) { + background-color: rgba($search-and-nav-links, 0.2); + } + + svg { + fill: currentColor; + } + + &.header-user-dropdown-toggle .header-user-notification-dot { + border-color: $nav-svg-color + 33; + } + } + } + + &.active > a, + &.dropdown.show > a { + color: $nav-svg-color; + background-color: $color-alternate; + + &:hover { + svg { + fill: $nav-svg-color; + } + } + + &.header-user-dropdown-toggle .header-user-notification-dot { + border-color: $white; + } + } + + .impersonated-user, + .impersonated-user:hover { + svg { + fill: $nav-svg-color; + } + } + } + } + } + + .navbar .title { + > a { + &:hover, + &:focus { + background-color: rgba($search-and-nav-links, 0.2); + } + } + } + + .search { + form { + background-color: rgba($search-and-nav-links, 0.2); + + &:hover { + background-color: rgba($search-and-nav-links, 0.3); + } + } + + .search-input::placeholder { + color: rgba($search-and-nav-links, 0.8); + } + + .search-input-wrap { + .search-icon, + .clear-icon { + fill: rgba($search-and-nav-links, 0.8); + } + } + + &.search-active { + form { + background-color: $white; + } + + .search-input-wrap { + .search-icon { + fill: rgba($search-and-nav-links, 0.8); + } + } + } + } + + // Sidebar + .nav-sidebar li.active { + box-shadow: inset 4px 0 0 $border-and-box-shadow; + + > a { + color: $sidebar-text; + } + + .nav-icon-container svg { + fill: $sidebar-text; + } + } + + .sidebar-top-level-items > li.active .badge.badge-pill { + color: $sidebar-text; + } + + .nav-links li { + &.active a, + &.md-header-tab.active button, + a.active { + border-bottom: 2px solid $active-tab-border; + + .badge.badge-pill { + font-weight: $gl-font-weight-bold; + } + } + } + + .branch-header-title { + color: $border-and-box-shadow; + } + + .ide-sidebar-link { + &.active { + color: $border-and-box-shadow; + box-shadow: inset 3px 0 $border-and-box-shadow; + + &.is-right { + box-shadow: inset -3px 0 $border-and-box-shadow; + } + } + } +} diff --git a/app/assets/stylesheets/themes/theme_indigo.scss b/app/assets/stylesheets/themes/theme_indigo.scss new file mode 100644 index 00000000000..bbf14afcca2 --- /dev/null +++ b/app/assets/stylesheets/themes/theme_indigo.scss @@ -0,0 +1,14 @@ +@import './theme_helper'; + +body { + &.ui-indigo { + @include gitlab-theme( + $indigo-200, + $indigo-500, + $indigo-700, + $indigo-800, + $indigo-900, + $white + ); + } +} diff --git a/app/assets/stylesheets/themes/theme_light.scss b/app/assets/stylesheets/themes/theme_light.scss new file mode 100644 index 00000000000..58003db4236 --- /dev/null +++ b/app/assets/stylesheets/themes/theme_light.scss @@ -0,0 +1,129 @@ +@import './theme_helper'; + +body { + &.ui-light { + @include gitlab-theme( + $gray-500, + $gray-700, + $gray-500, + $gray-500, + $gray-50, + $gray-500 + ); + + .navbar-gitlab { + background-color: $gray-50; + box-shadow: 0 1px 0 0 $border-color; + + .logo-text svg { + fill: $gray-900; + } + + .navbar-sub-nav, + .navbar-nav { + > li { + > a:hover, + > a:focus, + > button:hover { + color: $gray-900; + } + + &.active > a, + &.active > a:hover, + &.active > button { + color: $white; + } + } + } + + .container-fluid { + .navbar-toggler, + .navbar-toggler:hover { + color: $gray-500; + border-left: 1px solid $gray-100; + } + } + } + + .search { + form { + background-color: $white; + box-shadow: inset 0 0 0 1px $border-color; + + &:hover { + background-color: $white; + box-shadow: inset 0 0 0 1px $blue-200; + } + } + + .search-input-wrap { + .search-icon { + fill: $gray-100; + } + + .search-input { + color: $gl-text-color; + } + } + } + + .nav-sidebar li.active { + > a { + color: $gray-900; + } + + svg { + fill: $gray-900; + } + } + + .sidebar-top-level-items > li.active .badge.badge-pill { + color: $gray-900; + } + } + + &.gl-dark { + .logo-text svg { + fill: $gl-text-color; + } + + .navbar-gitlab { + background-color: $gray-50; + + .navbar-sub-nav, + .navbar-nav { + li { + > a:hover, + > a:focus, + > button:hover, + > button:focus { + color: $gl-text-color; + background-color: $gray-200; + } + } + + li.active, + li.dropdown.show { + > a, + > button { + color: $gl-text-color; + background-color: $gray-200; + } + } + } + + .search { + form { + background-color: $gray-100; + box-shadow: inset 0 0 0 1px $border-color; + + &:active, + &:hover { + background-color: $gray-100; + box-shadow: inset 0 0 0 1px $blue-200; + } + } + } + } + } +} diff --git a/app/assets/stylesheets/themes/theme_light_blue.scss b/app/assets/stylesheets/themes/theme_light_blue.scss new file mode 100644 index 00000000000..07d1c60a4c6 --- /dev/null +++ b/app/assets/stylesheets/themes/theme_light_blue.scss @@ -0,0 +1,14 @@ +@import './theme_helper'; + +body { + &.ui-light-blue { + @include gitlab-theme( + $theme-light-blue-200, + $theme-light-blue-500, + $theme-light-blue-500, + $theme-light-blue-700, + $theme-light-blue-700, + $white + ); + } +} diff --git a/app/assets/stylesheets/themes/theme_light_green.scss b/app/assets/stylesheets/themes/theme_light_green.scss new file mode 100644 index 00000000000..e122501b93c --- /dev/null +++ b/app/assets/stylesheets/themes/theme_light_green.scss @@ -0,0 +1,14 @@ +@import './theme_helper'; + +body { + &.ui-light-green { + @include gitlab-theme( + $theme-green-200, + $theme-green-500, + $theme-green-500, + $theme-light-green-700, + $theme-light-green-700, + $white + ); + } +} diff --git a/app/assets/stylesheets/themes/theme_light_indigo.scss b/app/assets/stylesheets/themes/theme_light_indigo.scss new file mode 100644 index 00000000000..5b607238ed9 --- /dev/null +++ b/app/assets/stylesheets/themes/theme_light_indigo.scss @@ -0,0 +1,14 @@ +@import './theme_helper'; + +body { + &.ui-light-indigo { + @include gitlab-theme( + $indigo-200, + $indigo-500, + $indigo-500, + $indigo-700, + $indigo-700, + $white + ); + } +} diff --git a/app/assets/stylesheets/themes/theme_light_red.scss b/app/assets/stylesheets/themes/theme_light_red.scss new file mode 100644 index 00000000000..fd3980183f3 --- /dev/null +++ b/app/assets/stylesheets/themes/theme_light_red.scss @@ -0,0 +1,14 @@ +@import './theme_helper'; + +body { + &.ui-light-red { + @include gitlab-theme( + $theme-light-red-200, + $theme-light-red-500, + $theme-light-red-500, + $theme-light-red-700, + $theme-light-red-700, + $white + ); + } +} diff --git a/app/assets/stylesheets/themes/theme_red.scss b/app/assets/stylesheets/themes/theme_red.scss new file mode 100644 index 00000000000..fa5ecc09f50 --- /dev/null +++ b/app/assets/stylesheets/themes/theme_red.scss @@ -0,0 +1,14 @@ +@import './theme_helper'; + +body { + &.ui-red { + @include gitlab-theme( + $theme-red-200, + $theme-red-500, + $theme-red-700, + $theme-red-800, + $theme-red-900, + $white + ); + } +} diff --git a/app/controllers/oauth/jira/authorizations_controller.rb b/app/controllers/oauth/jira/authorizations_controller.rb index a3e30ffc993..f552b0dc10c 100644 --- a/app/controllers/oauth/jira/authorizations_controller.rb +++ b/app/controllers/oauth/jira/authorizations_controller.rb @@ -14,6 +14,7 @@ class Oauth::Jira::AuthorizationsController < ApplicationController redirect_to oauth_authorization_path(client_id: params['client_id'], response_type: 'code', + scope: params['scope'], redirect_uri: oauth_jira_callback_url) end diff --git a/app/helpers/preferences_helper.rb b/app/helpers/preferences_helper.rb index 5a917a02d51..ff1f9d6d9fe 100644 --- a/app/helpers/preferences_helper.rb +++ b/app/helpers/preferences_helper.rb @@ -61,6 +61,10 @@ module PreferencesHelper @user_application_theme ||= Gitlab::Themes.for_user(current_user).css_class end + def user_application_theme_name + @user_application_theme_name ||= Gitlab::Themes.for_user(current_user).name.downcase.tr(' ', '_') + end + def user_color_scheme Gitlab::ColorSchemes.for_user(current_user).css_class end diff --git a/app/models/concerns/enums/prometheus_metric.rb b/app/models/concerns/enums/prometheus_metric.rb index 3dbcc8ee587..e65a01990a3 100644 --- a/app/models/concerns/enums/prometheus_metric.rb +++ b/app/models/concerns/enums/prometheus_metric.rb @@ -20,7 +20,8 @@ module Enums { business: 0, response: 1, - system: 2 + system: 2, + custom: 3 }.freeze end @@ -79,7 +80,11 @@ module Enums system: { group_title: _('System metrics (Custom)'), priority: -10 - }.freeze + }.freeze, + custom: { + group_title: _('Custom metrics'), + priority: 0 + } }.freeze end end diff --git a/app/models/concerns/mentionable/reference_regexes.rb b/app/models/concerns/mentionable/reference_regexes.rb index f44a674b3c9..307d58a3a3c 100644 --- a/app/models/concerns/mentionable/reference_regexes.rb +++ b/app/models/concerns/mentionable/reference_regexes.rb @@ -30,7 +30,7 @@ module Mentionable def self.external_pattern strong_memoize(:external_pattern) do issue_pattern = IssueTrackerService.reference_pattern - link_patterns = URI.regexp(%w(http https)) + link_patterns = URI::DEFAULT_PARSER.make_regexp(%w(http https)) reference_pattern(link_patterns, issue_pattern) end end diff --git a/app/models/prometheus_metric.rb b/app/models/prometheus_metric.rb index ea5e11c7a05..9ddf66cd388 100644 --- a/app/models/prometheus_metric.rb +++ b/app/models/prometheus_metric.rb @@ -16,11 +16,13 @@ class PrometheusMetric < ApplicationRecord validates :project, presence: true, unless: :common? validates :project, absence: true, if: :common? + scope :for_dashboard_path, -> (dashboard_path) { where(dashboard_path: dashboard_path) } scope :for_project, -> (project) { where(project: project) } scope :for_group, -> (group) { where(group: group) } scope :for_title, -> (title) { where(title: title) } scope :for_y_label, -> (y_label) { where(y_label: y_label) } scope :for_identifier, -> (identifier) { where(identifier: identifier) } + scope :not_identifier, -> (identifier) { where.not(identifier: identifier) } scope :common, -> { where(common: true) } scope :ordered, -> { reorder(created_at: :asc) } diff --git a/app/services/ci/process_pipeline_service.rb b/app/services/ci/process_pipeline_service.rb index d84ef5fbb93..18bae26613f 100644 --- a/app/services/ci/process_pipeline_service.rb +++ b/app/services/ci/process_pipeline_service.rb @@ -37,10 +37,12 @@ module Ci .pluck(Arel.sql('MAX(id)'), 'name') # mark builds that are retried - pipeline.statuses.latest - .where(name: latest_statuses.map(&:second)) - .where.not(id: latest_statuses.map(&:first)) - .update_all(retried: true) if latest_statuses.any? + if latest_statuses.any? + pipeline.statuses.latest + .where(name: latest_statuses.map(&:second)) + .where.not(id: latest_statuses.map(&:first)) + .update_all(retried: true) + end end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/services/projects/download_service.rb b/app/services/projects/download_service.rb index aba175eb79b..9810db84605 100644 --- a/app/services/projects/download_service.rb +++ b/app/services/projects/download_service.rb @@ -27,7 +27,7 @@ module Projects end def http?(url) - url =~ /\A#{URI.regexp(%w(http https))}\z/ + url =~ /\A#{URI::DEFAULT_PARSER.make_regexp(%w(http https))}\z/ end def valid_domain?(url) diff --git a/app/views/admin/applications/_form.html.haml b/app/views/admin/applications/_form.html.haml index 8338401bea5..0d01f1c57e0 100644 --- a/app/views/admin/applications/_form.html.haml +++ b/app/views/admin/applications/_form.html.haml @@ -16,11 +16,6 @@ = doorkeeper_errors_for application, :redirect_uri %span.form-text.text-muted Use one line per URI - - if Doorkeeper.configuration.native_redirect_uri - %span.form-text.text-muted - Use - %code= Doorkeeper.configuration.native_redirect_uri - for local tests = content_tag :div, class: 'form-group row' do .col-sm-2.col-form-label.pt-0 diff --git a/app/views/doorkeeper/applications/_form.html.haml b/app/views/doorkeeper/applications/_form.html.haml index 7fbaa35d1d5..f99db696fd6 100644 --- a/app/views/doorkeeper/applications/_form.html.haml +++ b/app/views/doorkeeper/applications/_form.html.haml @@ -11,9 +11,6 @@ %span.form-text.text-muted = _('Use one line per URI') - - if Doorkeeper.configuration.native_redirect_uri - %span.form-text.text-muted - = html_escape(_('Use %{native_redirect_uri} for local tests')) % { native_redirect_uri: tag.code(Doorkeeper.configuration.native_redirect_uri) } .form-group.form-check = f.check_box :confidential, class: 'form-check-input' diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index af1dd25eb3f..c861f475cac 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -49,6 +49,8 @@ = stylesheet_link_tag_defer "application_dark" - else = stylesheet_link_tag_defer "application" + - unless use_startup_css? + = stylesheet_link_tag_defer "themes/theme_#{user_application_theme_name}" = stylesheet_link_tag "disable_animations", media: "all" if Rails.env.test? || Gitlab.config.gitlab['disable_animations'] = stylesheet_link_tag_defer 'performance_bar' if performance_bar_enabled? diff --git a/app/views/layouts/_startup_css.haml b/app/views/layouts/_startup_css.haml index 47755d1cf24..ea05157ed19 100644 --- a/app/views/layouts/_startup_css.haml +++ b/app/views/layouts/_startup_css.haml @@ -3,4 +3,5 @@ - startup_filename = current_path?("sessions#new") ? 'signin' : user_application_theme == 'gl-dark' ? 'dark' : 'general' %style{ type: "text/css" } + = Rails.application.assets_manifest.find_sources("themes/theme_#{user_application_theme_name}.css").first.to_s.html_safe = Rails.application.assets_manifest.find_sources("startup/startup-#{startup_filename}.css").first.to_s.html_safe diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml index 54ca8788864..472185f0b05 100644 --- a/app/views/profiles/preferences/show.html.haml +++ b/app/views/profiles/preferences/show.html.haml @@ -1,6 +1,9 @@ - page_title _('Preferences') - @content_class = "limit-container-width" unless fluid_layout +- Gitlab::Themes.each do |theme| + = stylesheet_link_tag "themes/theme_#{theme.css_class.gsub('ui-', '')}" + = form_for @user, url: profile_preferences_path, remote: true, method: :put, html: { class: 'row gl-mt-3 js-preferences-form' } do |f| .col-lg-4.application-theme#navigation-theme %h4.gl-mt-0 diff --git a/app/views/shared/notes/_hints.html.haml b/app/views/shared/notes/_hints.html.haml index 51c1ee0c4d1..3703cca2290 100644 --- a/app/views/shared/notes/_hints.html.haml +++ b/app/views/shared/notes/_hints.html.haml @@ -27,7 +27,7 @@ or %button.attach-new-file.markdown-selector{ type: 'button' }= _("attach a new file") - %button.btn.markdown-selector.button-attach-file.btn-link{ type: 'button', tabindex: '-1' } + %button.btn.markdown-selector.button-attach-file.btn-link{ type: 'button' } = sprite_icon('media') %span.text-attach-file<> = _("Attach a file") diff --git a/changelogs/unreleased/233507-set-not-null-on-mr-metrics-target-project-id.yml b/changelogs/unreleased/233507-set-not-null-on-mr-metrics-target-project-id.yml new file mode 100644 index 00000000000..d9b48bd92b6 --- /dev/null +++ b/changelogs/unreleased/233507-set-not-null-on-mr-metrics-target-project-id.yml @@ -0,0 +1,5 @@ +--- +title: Add NOT NULL constraint to merge_request_metrics.target_project_id +merge_request: 40836 +author: +type: other diff --git a/changelogs/unreleased/238111-count-of-the-number-of-self-hosted-smtp-configurations-by-package.yml b/changelogs/unreleased/238111-count-of-the-number-of-self-hosted-smtp-configurations-by-package.yml new file mode 100644 index 00000000000..2999443a71f --- /dev/null +++ b/changelogs/unreleased/238111-count-of-the-number-of-self-hosted-smtp-configurations-by-package.yml @@ -0,0 +1,5 @@ +--- +title: Add smtp_server to usage ping data +merge_request: 39844 +author: +type: changed diff --git a/changelogs/unreleased/cngo-make-mr-edit-file-upload-button-tab-accessible.yml b/changelogs/unreleased/cngo-make-mr-edit-file-upload-button-tab-accessible.yml new file mode 100644 index 00000000000..177a432ed76 --- /dev/null +++ b/changelogs/unreleased/cngo-make-mr-edit-file-upload-button-tab-accessible.yml @@ -0,0 +1,5 @@ +--- +title: Make file upload button on MR edit page tab accessible +merge_request: 40995 +author: +type: fixed diff --git a/changelogs/unreleased/commented-keyword.yml b/changelogs/unreleased/commented-keyword.yml new file mode 100644 index 00000000000..cb4d9eeb0c9 --- /dev/null +++ b/changelogs/unreleased/commented-keyword.yml @@ -0,0 +1,5 @@ +--- +title: Fix Style/CommentedKeyword cop +merge_request: 41119 +author: Rajendra Kadam +type: fixed diff --git a/changelogs/unreleased/id-bump-doorkeeper-5-3.yml b/changelogs/unreleased/id-bump-doorkeeper-5-3.yml new file mode 100644 index 00000000000..53c641aecbf --- /dev/null +++ b/changelogs/unreleased/id-bump-doorkeeper-5-3.yml @@ -0,0 +1,5 @@ +--- +title: Bump doorkeeper to 5.3.0 +merge_request: 40929 +author: +type: changed diff --git a/changelogs/unreleased/lint-uri-regex.yml b/changelogs/unreleased/lint-uri-regex.yml new file mode 100644 index 00000000000..78f113776cd --- /dev/null +++ b/changelogs/unreleased/lint-uri-regex.yml @@ -0,0 +1,5 @@ +--- +title: Fix Lint/UriRegexp cop +merge_request: 41117 +author: Rajendra Kadam +type: fixed diff --git a/changelogs/unreleased/multiline-if-modifier.yml b/changelogs/unreleased/multiline-if-modifier.yml new file mode 100644 index 00000000000..d31430ecad7 --- /dev/null +++ b/changelogs/unreleased/multiline-if-modifier.yml @@ -0,0 +1,5 @@ +--- +title: Fix Style/MultilineIfModifier cop +merge_request: 41113 +author: Rajendra Kadam +type: fixed diff --git a/config/application.rb b/config/application.rb index dbf7e5cecfd..55444d86a98 100644 --- a/config/application.rb +++ b/config/application.rb @@ -190,6 +190,8 @@ module Gitlab config.assets.precompile << "errors.css" config.assets.precompile << "jira_connect.js" + config.assets.precompile << "themes/*.css" + config.assets.precompile << "highlight/themes/*.css" # Import gitlab-svgs directly from vendored directory diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index ad0b0c2008f..2f98471772f 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -79,13 +79,6 @@ Doorkeeper.configure do # Check out the wiki for more information on customization access_token_methods :from_access_token_param, :from_bearer_authorization, :from_bearer_param - # Change the native redirect uri for client apps - # When clients register with the following redirect uri, they won't be redirected to any server and the authorization code will be displayed within the provider - # The value can be any string. Use nil to disable this feature. When disabled, clients must provide a valid URL - # (Similar behaviour: https://developers.google.com/accounts/docs/OAuth2InstalledApp#choosingredirecturi) - # - native_redirect_uri nil # 'urn:ietf:wg:oauth:2.0:oob' - # Specify what grant flows are enabled in array of Strings. The valid # strings and the flows they enable are: # diff --git a/config/locales/doorkeeper.en.yml b/config/locales/doorkeeper.en.yml index 8469b72c312..81e4f73e6b2 100644 --- a/config/locales/doorkeeper.en.yml +++ b/config/locales/doorkeeper.en.yml @@ -30,7 +30,6 @@ en: errors: messages: # Common error messages - invalid_request: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.' invalid_redirect_uri: 'The redirect URI included is not valid.' unauthorized_client: 'The client is not authorized to perform this request using this method.' access_denied: 'The resource owner or authorization server denied the request.' @@ -54,6 +53,12 @@ en: # Password Access token errors invalid_resource_owner: 'The provided resource owner credentials are not valid, or resource owner cannot be found' + invalid_request: + unknown: 'The request is missing a required parameter, includes an unsupported parameter value, or is otherwise malformed.' + missing_param: 'Missing required parameter: %{value}.' + not_support_pkce: 'Invalid code_verifier parameter. Server does not support pkce.' + request_not_authorized: 'Request need to be authorized. Required parameter for authorizing request is missing or invalid.' + invalid_token: revoked: "The access token was revoked" expired: "The access token expired" diff --git a/db/post_migrate/20200831065320_add_not_valid_not_null_constraint_to_mr_metrics.rb b/db/post_migrate/20200831065320_add_not_valid_not_null_constraint_to_mr_metrics.rb new file mode 100644 index 00000000000..35bfabc0358 --- /dev/null +++ b/db/post_migrate/20200831065320_add_not_valid_not_null_constraint_to_mr_metrics.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddNotValidNotNullConstraintToMrMetrics < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_not_null_constraint :merge_request_metrics, :target_project_id, validate: false + end + + def down + remove_not_null_constraint :merge_request_metrics, :target_project_id + end +end diff --git a/db/post_migrate/20200831065322_add_tmp_index_to_target_project_id.rb b/db/post_migrate/20200831065322_add_tmp_index_to_target_project_id.rb new file mode 100644 index 00000000000..5d6d098ebfe --- /dev/null +++ b/db/post_migrate/20200831065322_add_tmp_index_to_target_project_id.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AddTmpIndexToTargetProjectId < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + TMP_INDEX_NAME = 'tmp_index_on_mr_metrics_target_project_id_null' + DOWNTIME = false + + disable_ddl_transaction! + + def up + add_concurrent_index :merge_request_metrics, :id, where: 'target_project_id IS NULL', name: TMP_INDEX_NAME + end + + def down + remove_concurrent_index_by_name :merge_request_metrics, name: TMP_INDEX_NAME + end +end diff --git a/db/post_migrate/20200831065705_ensure_target_project_id_is_filled.rb b/db/post_migrate/20200831065705_ensure_target_project_id_is_filled.rb new file mode 100644 index 00000000000..9b267933b04 --- /dev/null +++ b/db/post_migrate/20200831065705_ensure_target_project_id_is_filled.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +class EnsureTargetProjectIdIsFilled < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + BACKGROUND_MIGRATION_CLASS = 'CopyMergeRequestTargetProjectToMergeRequestMetrics' + BATCH_SIZE = 1_000 + DOWNTIME = false + + disable_ddl_transaction! + + class MergeRequest < ActiveRecord::Base + self.table_name = 'merge_requests' + end + + class MergeRequestMetrics < ActiveRecord::Base + include EachBatch + + belongs_to :merge_request + + self.table_name = 'merge_request_metrics' + end + + def up + Gitlab::BackgroundMigration.steal(BACKGROUND_MIGRATION_CLASS) + + # Do a manual update in case we lost BG jobs. The expected record count should be 0 or very low. + MergeRequestMetrics.where(target_project_id: nil).each_batch do |scope| + query_for_cte = scope.joins(:merge_request).select( + MergeRequestMetrics.arel_table[:id].as('id'), + MergeRequest.arel_table[:target_project_id].as('target_project_id') + ) + + MergeRequestMetrics.connection.execute <<-SQL + WITH target_project_id_and_metrics_id as ( + #{query_for_cte.to_sql} + ) + UPDATE #{MergeRequestMetrics.connection.quote_table_name(MergeRequestMetrics.table_name)} + SET target_project_id = target_project_id_and_metrics_id.target_project_id + FROM target_project_id_and_metrics_id + WHERE merge_request_metrics.id = target_project_id_and_metrics_id.id + SQL + end + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20200831074356_validate_not_null_constraint_on_mr_metrics.rb b/db/post_migrate/20200831074356_validate_not_null_constraint_on_mr_metrics.rb new file mode 100644 index 00000000000..8fd54186db3 --- /dev/null +++ b/db/post_migrate/20200831074356_validate_not_null_constraint_on_mr_metrics.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +class ValidateNotNullConstraintOnMrMetrics < ActiveRecord::Migration[6.0] + include Gitlab::Database::MigrationHelpers + + TMP_INDEX_NAME = 'tmp_index_on_mr_metrics_target_project_id_null' + DOWNTIME = false + + disable_ddl_transaction! + + def up + validate_not_null_constraint :merge_request_metrics, :target_project_id + + remove_concurrent_index_by_name :merge_request_metrics, name: TMP_INDEX_NAME + end + + def down + add_concurrent_index :merge_request_metrics, :id, where: 'target_project_id IS NULL', name: TMP_INDEX_NAME + end +end diff --git a/db/schema_migrations/20200831065320 b/db/schema_migrations/20200831065320 new file mode 100644 index 00000000000..127a6a18d9d --- /dev/null +++ b/db/schema_migrations/20200831065320 @@ -0,0 +1 @@ +3e704cf329786e89f43fdefbc6a91272bebc6af5653dd83b5a81567937f75752 \ No newline at end of file diff --git a/db/schema_migrations/20200831065322 b/db/schema_migrations/20200831065322 new file mode 100644 index 00000000000..ab56be867e8 --- /dev/null +++ b/db/schema_migrations/20200831065322 @@ -0,0 +1 @@ +4cb28d6da005682bb077427f4f544996065e86f0e76d5de98fc1761555a0535b \ No newline at end of file diff --git a/db/schema_migrations/20200831065705 b/db/schema_migrations/20200831065705 new file mode 100644 index 00000000000..385a9511d91 --- /dev/null +++ b/db/schema_migrations/20200831065705 @@ -0,0 +1 @@ +c1457272bd8f0055992df5d4a8ba1a62cb74d3af3fff25447b3abd2eb090841e \ No newline at end of file diff --git a/db/schema_migrations/20200831074356 b/db/schema_migrations/20200831074356 new file mode 100644 index 00000000000..cea1905fd1b --- /dev/null +++ b/db/schema_migrations/20200831074356 @@ -0,0 +1 @@ +8605268a026d4c66653008bb051c567c251044232b925b89f4407f9ad70f639c \ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 97d74287694..b0837b8275a 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -13209,7 +13209,8 @@ CREATE TABLE public.merge_request_metrics ( first_reassigned_at timestamp with time zone, added_lines integer, removed_lines integer, - target_project_id integer + target_project_id integer, + CONSTRAINT check_e03d0900bf CHECK ((target_project_id IS NOT NULL)) ); CREATE SEQUENCE public.merge_request_metrics_id_seq diff --git a/doc/administration/feature_flags.md b/doc/administration/feature_flags.md index 7cc7692975a..a8a14063f26 100644 --- a/doc/administration/feature_flags.md +++ b/doc/administration/feature_flags.md @@ -1,7 +1,7 @@ --- -stage: Release -group: Progressive Delivery -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 +stage: none +group: Development +info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines" type: reference description: "GitLab administrator: enable and disable GitLab features deployed behind feature flags" --- diff --git a/doc/development/documentation/styleguide.md b/doc/development/documentation/styleguide.md index ea13f6beb11..48c1ad0dcca 100644 --- a/doc/development/documentation/styleguide.md +++ b/doc/development/documentation/styleguide.md @@ -12,12 +12,23 @@ files. For broader information about the documentation, see the [Documentation guidelines](index.md). -For programmatic help adhering to the guidelines, see [Testing](index.md#testing). +For guidelines specific to text in the GitLab interface, see the Pajamas [Content](https://design.gitlab.com/content/error-messages) section. -See the GitLab Handbook for additional [writing style guidelines](https://about.gitlab.com/handbook/communication/#writing-style-guidelines) -that apply to all GitLab content, not just documentation. +For information on how to validate styles locally or by using GitLab CI/CD, see [Testing](index.md#testing). -View a list of [recent style guide updates](https://gitlab.com/dashboard/merge_requests?scope=all&utf8=%E2%9C%93&state=merged&label_name[]=tw-style¬[label_name][]=docs%3A%3Afix). +Use this guide for standards on grammar, formatting, word usage, and more. + +You can also view a list of [recent updates to this guide](https://gitlab.com/dashboard/merge_requests?scope=all&utf8=%E2%9C%93&state=merged&label_name[]=tw-style¬[label_name][]=docs%3A%3Afix). + +If you can't find what you need: + +- View the GitLab Handbook for [writing style guidelines](https://about.gitlab.com/handbook/communication/#writing-style-guidelines) that apply to all GitLab content. +- Refer to one of the following: + + - [Microsoft Style Guide](https://docs.microsoft.com/en-us/style-guide/). + - [Google Developer Documentation Style Guide](https://developers.google.com/style). + +If you have questions about style, mention `@tw-style` in an issue or merge request, or, if you have access to the GitLab Slack workspace, use `#docs-process`. ## Documentation is the single source of truth (SSOT) diff --git a/doc/development/feature_flags/index.md b/doc/development/feature_flags/index.md index 7e7ed02b955..2643571aec3 100644 --- a/doc/development/feature_flags/index.md +++ b/doc/development/feature_flags/index.md @@ -7,16 +7,40 @@ info: "See the Technical Writers assigned to Development Guidelines: https://abo # Feature flags in development of GitLab -[Feature Flags](../../operations/feature_flags.md) -can be used to gradually roll out changes, be -it a new feature, or a performance improvement. By using feature flags, we can -comfortably measure the impact of our changes, while still being able to easily -disable those changes, without having to revert an entire release. +Feature flags can be used to gradually deploy changes, regardless of whether +they are new features or performance improvements. By using feature flags, +you can determine the impact of GitLab-directed changes, while still being able +to disable those changes without having to revert an entire release. -Before using feature flags for GitLab's development, read through the following: +Before using feature flags for GitLab's development, review the following development guides: -- [Process for using features flags](process.md). -- [Developing with feature flags](development.md). -- [Controlling feature flags](controls.md). -- [Documenting features deployed behind feature flags](../documentation/feature_flags.md). -- [How GitLab administrators can enable and disable features behind flags](../../administration/feature_flags.md). +NOTE: **Note:** +The feature flags used by GitLab to deploy its own features **are not** the same +as the [feature flags offered as part of the product](../../operations/feature_flags.md). + +For an overview about starting with feature flags in GitLab's development, +use this [training template](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/.gitlab/issue_templates/feature-flag-training.md). + +Development guides: + +- [Process for using features flags](process.md): When you should use + feature flags in the development of GitLab, what's the cost of using them, + and how to include them in a release. +- [Developing with feature flags](development.md): Learn about the types of + feature flags, their definition and validation, how to create them, frontend and + backend details, and other information. +- [Documenting features deployed behind feature flags](../documentation/feature_flags.md): + How to document features deployed behind feature flags, and how to update the + documentation for features' flags when their states change. +- [Controlling feature flags](controls.md): Learn the process for deploying + a new feature, enabling it on GitLab.com, communicating the change, + logging, and cleaning up. + +User guides: + +- [How GitLab administrators can enable and disable features behind flags](../../administration/feature_flags.md): + An explanation for GitLab administrators about how they can + enable or disable GitLab features behind feature flags. +- [What "features deployed behind flags" means to the GitLab user](../../user/feature_flags.md): + An explanation for GitLab users regarding how certain features + might not be available to them until they are enabled. diff --git a/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md b/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md index 2cf2bb5b1d0..4f89aa4e4d8 100644 --- a/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md +++ b/doc/development/testing_guide/end_to_end/running_tests_that_require_special_setup.md @@ -134,3 +134,128 @@ Once you have finished testing you can stop and remove the Docker containers: docker stop gitlab-gitaly-ha praefect postgres gitaly3 gitaly2 gitaly1 docker rm gitlab-gitaly-ha praefect postgres gitaly3 gitaly2 gitaly1 ``` + +## Guide to run and debug monitor tests + +## How to set up + +To run the Monitor tests locally, against the GDK, please follow the preparation steps below: + +1. Complete the [Prerequisites](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/doc/howto/auto_devops/index.md#prerequisites-for-gitlab-team-members-only), at least through step 5. Note that the monitor tests do not require permissions to work with GKE because they use [k3s as a Kubernetes cluster provider](https://github.com/rancher/k3s). +1. The test setup deploys the app in a Kubernetes cluster, using the Auto DevOps deployment strategy. +To enable Auto DevOps in GDK, follow the [associated setup](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/doc/howto/auto_devops/index.md#setup) instructions. If you have problems, review the [troubleshooting guide](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/doc/howto/auto_devops/tips_and_troubleshooting.md) or reach out to the `#gdk` channel in the internal GitLab Slack. +1. Do [secure your GitLab instance](https://gitlab.com/gitlab-org/gitlab-development-kit/-/blob/master/doc/howto/auto_devops/index.md#secure-your-gitlab-instance) since it is now publicly accessible on `https://[YOUR-PORT].qa-tunnel.gitlab.info`. +1. Install the Kubernetes command line tool known as `kubectl`. Use the [official installation instructions](https://kubernetes.io/docs/tasks/tools/install-kubectl/). + +You might see NGINX issues when you run `gdk start` or `gdk restart`. In that case, run `sft login` to revalidate your credentials and regain access the QA Tunnel. + +## How to run + +Navigate to the folder in `/your-gdk/gitlab/qa` and issue the command: + +```shell +QA_DEBUG=true CHROME_HEADLESS=false GITLAB_ADMIN_USERNAME=rootusername GITLAB_ADMIN_PASSWORD=rootpassword GITLAB_QA_ACCESS_TOKEN=your_token_here GITLAB_QA_ADMIN_ACCESS_TOKEN=your_token_here CLUSTER_API_URL=https://kubernetes.docker.internal:6443 bundle exec bin/qa Test::Instance::All https://[YOUR-PORT].qa-tunnel.gitlab.info/ -- qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb --tag kubernetes --tag orchestrated --tag requires_admin +``` + +The following includes more information on the command: + +-`QA_DEBUG` - Set to `true` to verbosely log page object actions. +-`CHROME_HEADLESS` - When running locally, set to `false` to allow Chrome tests to be visible - watch your tests being run. +-`GITLAB_ADMIN_USERNAME` - Admin username to use when adding a license. +-`GITLAB_ADMIN_PASSWORD` - Admin password to use when adding a license. +-`GITLAB_QA_ACCESS_TOKEN` and `GITLAB_QA_ADMIN_ACCESS_TOKEN` - A valid personal access token with the `api` scope. This is used for API access during tests, and is used in the version that staging is currently running. The `ADMIN_ACCESS_TOKEN` is from a user with admin access. Used for API access as an admin during tests. +-`CLUSTER_API_URL` - Use the address `https://kubernetes.docker.internal:6443` . This address is used to enable the cluster to be network accessible while deploying using Auto DevOps. +-`https://[YOUR-PORT].qa-tunnel.gitlab.info/` - The address of your local GDK +-`qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb` - The path to the monitor core specs +-`--tag` - the meta-tags used to filter the specs correctly + +At the moment of this writing, there are two specs which run monitor tests: + +-`qa/specs/features/browser_ui/8_monitor/all_monitor_core_features_spec.rb` - has the specs of features in GitLab Core +-`qa/specs/features/ee/browser_ui/8_monitor/all_monitor_features_spec.rb` - has the specs of features for paid GitLab (Enterprise Edition) + +## How to debug + +The monitor tests follow this setup flow: + +1. Creates a k3s cluster on your local machine. +1. Creates a project that has Auto DevOps enabled and uses an Express template (NodeJS) for the app to be deployed. +1. Associates the created cluster to the project and installs GitLab Runner, Prometheus and Ingress which are the needed components for a successful deployment. +1. Creates a CI pipeline with 2 jobs (`build` and `production`) to deploy the app on the Kubernetes cluster. +1. Goes to Operation > Metrics menu to verify data is being received and the app is being monitored successfully. + +The test requires a number of components. The setup requires time to collect the metrics of a real deployment. +The complexity of the setup may lead to problems unrelated to the app. The following sections include common strategies to debug possible issues. + +### Deployment with Auto DevOps + +When debugging issues in the CI or locally in the CLI, open the Kubernetes job in the pipeline. +In the job log window, click on the top right icon labeled as *"Show complete raw"* to reveal raw job logs. +You can now search through the logs for *Job log*, which matches delimited sections like this one: + +```shell +------- Job log: ------- +``` + +A Job log is a subsection within these logs, related to app deployment. We use two jobs: `build` and `production`. +You can find the root causes of deployment failures in these logs, which can compromise the entire test. +If a `build` job fails, the `production` job won't run, and the test fails. + +The long test setup does not take screenshots of failures, which is a known [issue](https://gitlab.com/gitlab-org/quality/team-tasks/-/issues/270). +However, if the spec fails (after a successful deployment) then you should be able to find screenshots which display the feature failure. +To access them in CI, go to the main job log window, look on the left side panel's Job artifacts section, and click Browse. + +### Common issues + +**Container Registry** + +When enabling Auto DevOps in the GDK, you may see issues with the Container Registry, which stores +images of the app to be deployed. + +You can access the Registry is available by opening an existing project. On the left hand menu, +select `Packages & Registries > Container Registries`. If the Registry is available, this page should load normally. + +Also, the Registry should be running in Docker: + +```shell +$ docker ps + +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +f035f339506c registry.gitlab.com/gitlab-org/build/cng/gitlab-container-registry:v2.9.1-gitlab "/bin/sh -c 'exec /b…" 3 hours ago Up 3 hours 0.0.0.0:5000->5000/tcp jovial_proskuriakova +``` + +The `gdk status` command shows if the registry is running: + +```shell +run: ./services/registry: (pid 2662) 10875s, normally down; run: log: (pid 65148) 177993s +run: ./services/tunnel_gitlab: (pid 2650) 10875s, normally down; run: log: (pid 65154) 177993s +run: ./services/tunnel_registry: (pid 2651) 10875s, normally down; run: log: (pid 65155) 177993s +``` + +Also, restarting Docker and then, on the Terminal, issue the command `docker login https://[YOUR-REGISTRY-PORT].qa-tunnel.gitlab.info:443` and use the GDK credentials to login. Note that the Registry port and GDK port are not the same. When configuring Auto DevOps in GDK, the `gdk reconfigure` command outputs the port of the Registry: + +```shell +********************************************* +Tunnel URLs + +GitLab: https://[PORT].qa-tunnel.gitlab.info +Registry: https://[PORT].qa-tunnel.gitlab.info +********************************************* +``` + +These Tunnel URLs are used by the QA SSH Tunnel generated when enabling Auto DevOps on the GDK. + +**Pod Eviction** + +Pod eviction happens when a node in a Kubernetes cluster is running out of memory or disk. After many local deployments this issue can happen. The UI shows that installing Prometheus, GitLab Runner and Ingress failed. How to be sure it is an Eviction? While the test is running, open another Terminal window and debug the current Kubernetes cluster by `kubectl get pods --all-namespaces`. If you observe that Pods have *Evicted status* such as the install-runner here: + +```shell +$ kubectl get pods --all-namespaces + +NAMESPACE NAME READY STATUS RESTARTS AGE +gitlab-managed-apps install-ingress 0/1 Pending 0 25s +gitlab-managed-apps install-prometheus 0/1 Pending 0 12s +gitlab-managed-apps install-runner 0/1 Evicted 0 75s +``` + +You can free some memory with either of the following commands: `docker prune system` or `docker prune volume`. diff --git a/doc/integration/elasticsearch.md b/doc/integration/elasticsearch.md index 4d0ed035ad5..1d245f50dc4 100644 --- a/doc/integration/elasticsearch.md +++ b/doc/integration/elasticsearch.md @@ -163,10 +163,15 @@ NOTE: **Note:** For large GitLab instances you can follow the instructions for [Indexing large instances](#indexing-large-instances) below. -In order to enable Elasticsearch, you need to have admin access in GitLab. +To enable Elasticsearch, you need to have admin access to GitLab: 1. Navigate to **Admin Area** (wrench icon), then **Settings > General** - and expand the **Elasticsearch** section. + and expand the **Elasticsearch** section. + + NOTE: **Note:** + To see the Elasticsearch section, you need an active Starter + [license](../user/admin_area/license.md). + 1. Configure the [Elasticsearch settings](#elasticsearch-configuration) for your Elasticsearch cluster. Do not enable **Elasticsearch indexing** or **Search with Elasticsearch** yet. diff --git a/doc/operations/feature_flags.md b/doc/operations/feature_flags.md index 8c79079f595..c91c150061d 100644 --- a/doc/operations/feature_flags.md +++ b/doc/operations/feature_flags.md @@ -16,6 +16,10 @@ delivery from customer launch. For an example of feature flags in action, see [GitLab for Deploys, Feature Flags, and Error Tracking](https://www.youtube.com/embed/5tw2p6lwXxo). +NOTE: **Note:** +The Feature Flags GitLab offer as a feature (described in this document) is not the same method +used for the [development of GitLab](../development/feature_flags/index.md). + ## How it works GitLab uses [Unleash](https://github.com/Unleash/unleash), a feature diff --git a/doc/operations/incident_management/alertdetails.md b/doc/operations/incident_management/alert_details.md similarity index 90% rename from doc/operations/incident_management/alertdetails.md rename to doc/operations/incident_management/alert_details.md index 774eaee286f..2c5a8d704e4 100644 --- a/doc/operations/incident_management/alertdetails.md +++ b/doc/operations/incident_management/alert_details.md @@ -7,7 +7,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w # Alert details page Navigate to the Alert details view by visiting the -[Alert list](alerts.md) and selecting an alert from the +[Alert list](./alerts.md) and selecting an alert from the list. You need least Developer [permissions](../../user/permissions.md) to access alerts. @@ -18,16 +18,16 @@ amount of information you need. The **Overview** tab provides basic information about the alert: -![Alert Detail Overview](img/alert_detail_overview_v13_1.png) +![Alert Detail Overview](./img/alert_detail_overview_v13_1.png) ## Alert details tab -![Alert Full Details](img/alert_detail_full_v13_1.png) +![Alert Full Details](./img/alert_detail_full_v13_1.png) ### Update an Alert's status The Alert detail view enables you to update the Alert Status. -See [Create and manage alerts in GitLab](alerts.md) for more details. +See [Create and manage alerts in GitLab](./alerts.md) for more details. ### Create an Issue from an Alert @@ -57,11 +57,11 @@ GitLab currently only supports a single assignee per alert. 1. To display the list of current alerts, click **{cloud-gear}** **Operations > Alerts**: - ![Alert List View Assignee(s)](img/alert_list_assignees_v13_1.png) + ![Alert List View Assignee(s)](./img/alert_list_assignees_v13_1.png) 1. Select your desired alert to display its **Alert Details View**: - ![Alert Details View Assignee(s)](img/alert_details_assignees_v13_1.png) + ![Alert Details View Assignee(s)](./img/alert_details_assignees_v13_1.png) 1. If the right sidebar is not expanded, click **{angle-double-right}** **Expand sidebar** to expand it. @@ -69,7 +69,7 @@ GitLab currently only supports a single assignee per alert. dropdown menu, select each user you want to assign to the alert. GitLab creates a [To-Do list item](../../user/todos.md) for each user. - ![Alert Details View Assignee(s)](img/alert_todo_assignees_v13_1.png) + ![Alert Details View Assignee(s)](./img/alert_todo_assignees_v13_1.png) To remove an assignee, click **Edit** next to the **Assignee** dropdown menu and deselect the user from the list of assignees, or click **Unassigned**. @@ -88,7 +88,7 @@ The following actions will result in a system note: - [Creating an issue based on an alert](#create-an-issue-from-an-alert) - [Assignment of an alert to a user](#update-an-alerts-assignee) -![Alert Details View System Notes](img/alert_detail_system_notes_v13_1.png) +![Alert Details View System Notes](./img/alert_detail_system_notes_v13_1.png) ### Create a To-Do from an Alert @@ -102,11 +102,11 @@ Alert details screen, and view them later on your **To-Do List**. To add a To-Do 1. Select your desired alert to display its **Alert Management Details View**. 1. Click the **Add a To-Do** button in the right sidebar: - ![Alert Details Add A To Do](img/alert_detail_add_todo_v13_1.png) + ![Alert Details Add A To Do](./img/alert_detail_add_todo_v13_1.png) Click the **To-Do** **{todo-done}** in the navigation bar to view your current To-Do list. -![Alert Details Added to Do](img/alert_detail_added_todo_v13_1.png) +![Alert Details Added to Do](./img/alert_detail_added_todo_v13_1.png) ### View an Alert's metrics data @@ -145,7 +145,7 @@ notifications, simplifying communication and ownership of the alert. After completing their portion of investigating or fixing the alert, users can unassign their account from the alert when their role is complete. -The alert status can be updated on the [Alert list](alerts.md) to +The alert status can be updated on the [Alert list](./alerts.md) to reflect if the alert has been resolved. ## View an Alert's logs diff --git a/doc/operations/incident_management/alerts.md b/doc/operations/incident_management/alerts.md index 04077905af2..e7d81068858 100644 --- a/doc/operations/incident_management/alerts.md +++ b/doc/operations/incident_management/alerts.md @@ -14,7 +14,7 @@ but you can change the sort order by clicking the headers in the Alert Managemen The alert list displays the following information: -![Alert List](img/alert_list_v13_1.png) +![Alert List](./img/alert_list_v13_1.png) - **Search** - The alert list supports a simple free text search on the title, description, monitoring tool, and service fields. @@ -58,7 +58,7 @@ To populate the alerts with data, read You can configure an externally-managed Prometheus instance to send alerts to GitLab. To set up this configuration, read the [configuring Prometheus](../metrics/alerts.md#external-prometheus-instances) documentation. Activating the external Prometheus -configuration also enables the [Alerts list](alerts.md). +configuration also enables the [Alerts list](./alerts.md). To populate the alerts with data, read [External Prometheus instances](../metrics/alerts.md#external-prometheus-instances). @@ -69,9 +69,9 @@ GitLab provides the Generic Alerts endpoint so you can accept alerts from a thir alerts service. Read the [instructions for toggling generic alerts](generic_alerts.md#setting-up-generic-alerts) to add this option. After configuring the endpoint, the -[Alerts list](alerts.md) is enabled. +[Alerts list](./alerts.md) is enabled. -To populate the alerts with data, read [Customizing the payload](generic_alerts.md#customizing-the-payload) for requests to the alerts endpoint. +To populate the alerts with data, read [Customizing the payload](./generic_alerts.md#customizing-the-payload) for requests to the alerts endpoint. ### Opsgenie integration **(PREMIUM)** @@ -82,7 +82,7 @@ A new way of monitoring Alerts via a GitLab integration is with NOTE: **Note:** If you enable the Opsgenie integration, you can't have other GitLab alert services, -such as [Generic Alerts](generic_alerts.md) or +such as [Generic Alerts](./generic_alerts.md) or Prometheus alerts, active at the same time. To enable Opsgenie integration: @@ -104,7 +104,7 @@ Each level of alert contains a uniquely shaped and color-coded icon to help you identify the severity of a particular alert. These severity icons help you immediately identify which alerts you should prioritize investigating: -![Alert Management Severity System](img/alert_management_severity_v13_0.png) +![Alert Management Severity System](./img/alert_management_severity_v13_0.png) Alerts contain one of the following icons: diff --git a/doc/operations/incident_management/generic_alerts.md b/doc/operations/incident_management/generic_alerts.md index 56a2715ab04..dd3ac918767 100644 --- a/doc/operations/incident_management/generic_alerts.md +++ b/doc/operations/incident_management/generic_alerts.md @@ -117,9 +117,9 @@ In GitLab versions 13.2 and greater, GitLab groups alerts based on their payload When an incoming alert contains the same payload as another alert (excluding the `start_time` and `hosts` attributes), GitLab groups these alerts together and displays a counter on the -[Alert Management List](incidents.md) +[Alert Management List](./incidents.md) and details pages. If the existing alert is already `resolved`, then a new alert will be created instead. -![Alert Management List](img/alert_list_v13_1.png) +![Alert Management List](./img/alert_list_v13_1.png) diff --git a/doc/operations/incident_management/incidents.md b/doc/operations/incident_management/incidents.md index 0668dc72c22..103bc9c0264 100644 --- a/doc/operations/incident_management/incidents.md +++ b/doc/operations/incident_management/incidents.md @@ -13,7 +13,7 @@ For users with at least Developer [permissions](../../user/permissions.md), the Incident Management list is available at **Operations > Incidents** in your project's sidebar. The list contains the following metrics: -![Incident List](img/incident_list_sort_v13_3.png) +![Incident List](./img/incident_list_sort_v13_3.png) - **Status** - To filter incidents by their status, click **Open**, **Closed**, or **All** above the incident list. @@ -26,7 +26,7 @@ in your project's sidebar. The list contains the following metrics: tooltip depending on the user's locale. - **Assignees** - The user assigned to the incident. - **Published** - Displays a green check mark (**{check-circle}**) if the incident is published - to a [Status Page](status_page.md).. **(ULTIMATE)** + to a [Status Page](./status_page.md).. **(ULTIMATE)** The Incident list displays incidents sorted by incident created date. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/229534) to GitLab core in 13.3).) @@ -47,7 +47,7 @@ to create issues when alerts are triggered: 1. Navigate to **Settings > Operations > Incidents** and expand **Incidents**: - ![Incident Management Settings](img/incident_management_settings_v13_3.png) + ![Incident Management Settings](./img/incident_management_settings_v13_3.png) 1. For GitLab versions 11.11 and greater, you can select the **Create an issue** checkbox to create an issue based on your own @@ -91,7 +91,7 @@ in both PagerDuty and GitLab: 1. Navigate to **Settings > Operations > Incidents** and expand **Incidents**. 1. Select the **PagerDuty integration** tab: - ![PagerDuty incidents integration](img/pagerduty_incidents_integration_v13_3.png) + ![PagerDuty incidents integration](./img/pagerduty_incidents_integration_v13_3.png) 1. Activate the integration, and save the changes in GitLab. 1. Copy the value of **Webhook URL** for use in a later step. diff --git a/doc/operations/incident_management/index.md b/doc/operations/incident_management/index.md index aded62c07fa..1575c48492e 100644 --- a/doc/operations/incident_management/index.md +++ b/doc/operations/incident_management/index.md @@ -8,13 +8,13 @@ info: To determine the technical writer assigned to the Stage/Group associated w > [Introduced](https://gitlab.com/groups/gitlab-org/-/epics/2877) in GitLab 13.0. -Alert Management enables developers to easily discover and view the alerts +Incident Management enables developers to easily discover and view the alerts generated by their application. By surfacing alert information where the code is being developed, efficiency and awareness can be increased. GitLab offers solutions for handling incidents in your applications and services, such as [setting up Prometheus alerts](#configure-prometheus-alerts), -[displaying metrics](alertdetails.md#embed-metrics-in-incidents-and-issues), and sending notifications. +[displaying metrics](./alert_details.md#embed-metrics-in-incidents-and-issues), and sending notifications. ## Alert notifications @@ -35,7 +35,7 @@ These emails contain details of the alert, and a link for more information. To send separate email notifications to users with [Developer permissions](../../user/permissions.md), see -[Configure incidents](incidents.md#configure-incidents). +[Configure incidents](./incidents.md#configure-incidents). ## Configure Prometheus alerts @@ -49,9 +49,12 @@ user, but it does not count toward your license limit. ## Configure external generic alerts -GitLab can accept alerts from any source through a generic webhook receiver. When -[configuring the generic alerts integration](generic_alerts.md), -GitLab creates a unique endpoint which receives a JSON-formatted, customizable payload. +GitLab can accept alerts from any source through a generic webhook receiver. +When [configuring the generic alerts integration](./generic_alerts.md), GitLab +creates a unique endpoint which receives a JSON-formatted, customizable payload. + +After configuration, you can manage your alerts using either the +[alerts section](./alerts.md) or the [alert details section](./alert_details.md). ## Integrate incidents with Slack @@ -66,3 +69,13 @@ GitLab enables you to [associate a Zoom meeting with an issue](../../user/projec for synchronous communication during incident management. After starting a Zoom call for an incident, you can associate the conference call with an issue. Your team members can join the Zoom call without requesting a link. + +## More information + +For information about GitLab and incident management, see: + +- [Generic alerts](./generic_alerts.md) +- [Alerts](./alerts.md) +- [Alert details](./alert_details.md) +- [Incidents](./incidents.md) +- [Status page](./status_page.md) diff --git a/doc/operations/incident_management/status_page.md b/doc/operations/incident_management/status_page.md index e376607d86f..9db3593caec 100644 --- a/doc/operations/incident_management/status_page.md +++ b/doc/operations/incident_management/status_page.md @@ -12,11 +12,11 @@ With a GitLab Status Page, you can create and deploy a static website to communi efficiently to users during an incident. The Status Page landing page displays an overview of recent incidents: -![Status Page landing page](img/status_page_incidents_v12_10.png) +![Status Page landing page](./img/status_page_incidents_v12_10.png) Clicking an incident displays a detail page with more information about a particular incident: -![Status Page detail](img/status_page_detail_v12_10.png) +![Status Page detail](./img/status_page_detail_v12_10.png) - Status on the incident, including when the incident was last updated. - The incident title, including any emojis. @@ -144,7 +144,7 @@ you provided during setup. As part of publication, GitLab will: After publication, you can access the incident's details page by clicking the **Published on status page** button displayed under the Incident's title. -![Status Page detail link](img/status_page_detail_link_v13_1.png) +![Status Page detail link](./img/status_page_detail_link_v13_1.png) ### Update an incident diff --git a/doc/user/application_security/dependency_scanning/index.md b/doc/user/application_security/dependency_scanning/index.md index e8dbfb243f7..1350e2c6c25 100644 --- a/doc/user/application_security/dependency_scanning/index.md +++ b/doc/user/application_security/dependency_scanning/index.md @@ -155,7 +155,7 @@ The following variables allow configuration of global dependency scanning settin | `SECURE_ANALYZERS_PREFIX` | Override the name of the Docker registry providing the official default images (proxy). Read more about [customizing analyzers](analyzers.md). | | `DS_DEFAULT_ANALYZERS` | Override the names of the official default images. Read more about [customizing analyzers](analyzers.md). | | `DS_DISABLE_DIND` | Disable Docker-in-Docker and run analyzers [individually](#enabling-docker-in-docker). This variable is `true` by default. | -| `ADDITIONAL_CA_CERT_BUNDLE` | Bundle of CA certs to trust. | +| `ADDITIONAL_CA_CERT_BUNDLE` | Bundle of CA certs to trust. The bundle of certificates provided here is also used by other tools during the scanning process, such as `git`, `yarn`, or `npm`. | | `DS_EXCLUDED_PATHS` | Exclude vulnerabilities from output based on the paths. A comma-separated list of patterns. Patterns can be globs, or file or folder paths (for example, `doc,spec`). Parent directories also match patterns. Default: `"spec, test, tests, tmp"` | | `SECURE_LOG_LEVEL` | Set the minimum logging level. Messages of this logging level or higher are output. From highest to lowest severity, the logging levels are: `fatal`, `error`, `warn`, `info`, `debug`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/10880) in GitLab 13.1. Default: `info` | @@ -444,7 +444,6 @@ include: variables: SECURE_ANALYZERS_PREFIX: "docker-registry.example.com/analyzers" GEMNASIUM_DB_REMOTE_URL: "gitlab.example.com/gemnasium-db.git" - GIT_SSL_NO_VERIFY: "true" ``` See explanations of the variables above in the [configuration section](#configuration). diff --git a/doc/user/feature_flags.md b/doc/user/feature_flags.md index 17c33987793..c134b7c083c 100644 --- a/doc/user/feature_flags.md +++ b/doc/user/feature_flags.md @@ -1,7 +1,8 @@ --- -stage: Release -group: Progressive Delivery -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 +stage: none +group: Development +info: "See the Technical Writers assigned to Development Guidelines: https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments-to-development-guidelines" +description: "Understand what 'GitLab features deployed behind flags' means." --- # GitLab functionality may be limited by feature flags diff --git a/doc/user/project/repository/repository_mirroring.md b/doc/user/project/repository/repository_mirroring.md index bbb673b74b0..76ac221a295 100644 --- a/doc/user/project/repository/repository_mirroring.md +++ b/doc/user/project/repository/repository_mirroring.md @@ -45,6 +45,11 @@ The following are some possible use cases for repository mirroring: - You have old projects in another source that you don't use actively anymore, but don't want to remove for archiving purposes. In that case, you can create a push mirror so that your active GitLab repository can push its changes to the old location. +- You are a GitLab self-managed user for privacy reasons and your instance is closed to the public, + but you still have certain software components that you want open sourced. In this case, utilizing + GitLab to be your primary repository which is closed from the public, and using push mirroring to a + GitLab.com repository that's public, allows you to open source specific projects and contribute back + to the open source community. ## Pushing to a remote repository **(CORE)** diff --git a/lib/api/applications.rb b/lib/api/applications.rb index 4e8d68c8d09..4f2c3ee79ef 100644 --- a/lib/api/applications.rb +++ b/lib/api/applications.rb @@ -6,6 +6,15 @@ module API before { authenticated_as_admin! } resource :applications do + helpers do + def validate_redirect_uri(value) + uri = ::URI.parse(value) + !uri.is_a?(URI::HTTP) || uri.host + rescue URI::InvalidURIError + false + end + end + desc 'Create a new application' do detail 'This feature was introduced in GitLab 10.5' success Entities::ApplicationWithSecret @@ -19,6 +28,13 @@ module API desc: 'Application will be used where the client secret is confidential' end post do + # Validate that host in uri is specified + # Please remove it when https://github.com/doorkeeper-gem/doorkeeper/pull/1440 is merged + # and the doorkeeper gem version is bumped + unless validate_redirect_uri(declared_params[:redirect_uri]) + render_api_error!({ redirect_uri: ["must be an absolute URI."] }, :bad_request) + end + application = Doorkeeper::Application.new(declared_params) if application.save diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb index 140351c9e5c..9f5a6e87505 100644 --- a/lib/api/commit_statuses.rb +++ b/lib/api/commit_statuses.rb @@ -117,8 +117,10 @@ module API render_api_error!('invalid state', 400) end - MergeRequest.where(source_project: user_project, source_branch: ref) - .update_all(head_pipeline_id: pipeline.id) if pipeline.latest? + if pipeline.latest? + MergeRequest.where(source_project: user_project, source_branch: ref) + .update_all(head_pipeline_id: pipeline.id) + end present status, with: Entities::CommitStatus rescue StateMachines::InvalidTransition => e diff --git a/lib/gitlab/ci/pipeline/chain/config/content/remote.rb b/lib/gitlab/ci/pipeline/chain/config/content/remote.rb index dcc336b8929..4990a5a6eb5 100644 --- a/lib/gitlab/ci/pipeline/chain/config/content/remote.rb +++ b/lib/gitlab/ci/pipeline/chain/config/content/remote.rb @@ -9,7 +9,7 @@ module Gitlab class Remote < Source def content strong_memoize(:content) do - next unless ci_config_path =~ URI.regexp(%w[http https]) + next unless ci_config_path =~ URI::DEFAULT_PARSER.make_regexp(%w[http https]) YAML.dump('include' => [{ 'remote' => ci_config_path }]) end diff --git a/lib/gitlab/database_importers/common_metrics/prometheus_metric_enums.rb b/lib/gitlab/database_importers/common_metrics/prometheus_metric_enums.rb index fb0fcc5a93b..8a5f53be20f 100644 --- a/lib/gitlab/database_importers/common_metrics/prometheus_metric_enums.rb +++ b/lib/gitlab/database_importers/common_metrics/prometheus_metric_enums.rb @@ -18,6 +18,7 @@ module Gitlab business: 0, response: 1, system: 2, + custom: 3, cluster_health: -100 } @@ -34,7 +35,8 @@ module Gitlab aws_elb: _('Response metrics (AWS ELB)'), nginx: _('Response metrics (NGINX)'), kubernetes: _('System metrics (Kubernetes)'), - cluster_health: _('Cluster Health') + cluster_health: _('Cluster Health'), + custom: _('Custom metrics') } end end diff --git a/lib/gitlab/metrics/dashboard/importer.rb b/lib/gitlab/metrics/dashboard/importer.rb new file mode 100644 index 00000000000..ca835650648 --- /dev/null +++ b/lib/gitlab/metrics/dashboard/importer.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +module Gitlab + module Metrics + module Dashboard + class Importer + def initialize(dashboard_path, project) + @dashboard_path = dashboard_path.to_s + @project = project + end + + def execute + return false unless Dashboard::Validator.validate(dashboard_hash, project: project, dashboard_path: dashboard_path) + + Dashboard::Importers::PrometheusMetrics.new(dashboard_hash, project: project, dashboard_path: dashboard_path).execute + rescue Gitlab::Config::Loader::FormatError + false + end + + def execute! + Dashboard::Validator.validate!(dashboard_hash, project: project, dashboard_path: dashboard_path) + + Dashboard::Importers::PrometheusMetrics.new(dashboard_hash, project: project, dashboard_path: dashboard_path).execute! + end + + private + + attr_accessor :dashboard_path, :project + + def dashboard_hash + @dashboard_hash ||= begin + raw_dashboard = Dashboard::RepoDashboardFinder.read_dashboard(project, dashboard_path) + return unless raw_dashboard.present? + + ::Gitlab::Config::Loader::Yaml.new(raw_dashboard).load_raw! + end + end + end + end + end +end diff --git a/lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb b/lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb new file mode 100644 index 00000000000..d1490d5d9b6 --- /dev/null +++ b/lib/gitlab/metrics/dashboard/importers/prometheus_metrics.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +module Gitlab + module Metrics + module Dashboard + module Importers + class PrometheusMetrics + ALLOWED_ATTRIBUTES = %i(title query y_label unit legend group dashboard_path).freeze + + # Takes a JSON schema validated dashboard hash and + # imports metrics to database + def initialize(dashboard_hash, project:, dashboard_path:) + @dashboard_hash = dashboard_hash + @project = project + @dashboard_path = dashboard_path + end + + def execute + import + rescue ActiveRecord::RecordInvalid, ::Gitlab::Metrics::Dashboard::Transformers::TransformerError + false + end + + def execute! + import + end + + private + + attr_reader :dashboard_hash, :project, :dashboard_path + + def import + delete_stale_metrics + create_or_update_metrics + end + + # rubocop: disable CodeReuse/ActiveRecord + def create_or_update_metrics + # TODO: use upsert and worker for callbacks? + prometheus_metrics_attributes.each do |attributes| + prometheus_metric = PrometheusMetric.find_or_initialize_by(attributes.slice(:identifier, :project)) + prometheus_metric.update!(attributes.slice(*ALLOWED_ATTRIBUTES)) + end + end + # rubocop: enable CodeReuse/ActiveRecord + + def delete_stale_metrics + identifiers = prometheus_metrics_attributes.map { |metric_attributes| metric_attributes[:identifier] } + + stale_metrics = PrometheusMetric.for_project(project) + .for_dashboard_path(dashboard_path) + .for_group(Enums::PrometheusMetric.groups[:custom]) + .not_identifier(identifiers) + + # TODO: use destroy_all and worker for callbacks? + stale_metrics.each(&:destroy) + end + + def prometheus_metrics_attributes + @prometheus_metrics_attributes ||= begin + Dashboard::Transformers::Yml::V1::PrometheusMetrics.new( + dashboard_hash, + project: project, + dashboard_path: dashboard_path + ).execute + end + end + end + end + end + end +end diff --git a/lib/gitlab/metrics/dashboard/transformers/errors.rb b/lib/gitlab/metrics/dashboard/transformers/errors.rb new file mode 100644 index 00000000000..4d94ab098ae --- /dev/null +++ b/lib/gitlab/metrics/dashboard/transformers/errors.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Gitlab + module Metrics + module Dashboard + module Transformers + TransformerError = Class.new(StandardError) + + module Errors + class MissingAttribute < TransformerError + def initialize(attribute_name) + super("Missing attribute: '#{attribute_name}'") + end + end + end + end + end + end +end diff --git a/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb b/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb new file mode 100644 index 00000000000..4e46eec17d6 --- /dev/null +++ b/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +module Gitlab + module Metrics + module Dashboard + module Transformers + module Yml + module V1 + # Takes a JSON schema validated dashboard hash and + # maps it to PrometheusMetric model attributes + class PrometheusMetrics + def initialize(dashboard_hash, project: nil, dashboard_path: nil) + @dashboard_hash = dashboard_hash.with_indifferent_access + @project = project + @dashboard_path = dashboard_path + + @dashboard_hash.default_proc = -> (h, k) { raise Transformers::Errors::MissingAttribute, k.to_s } + end + + def execute + prometheus_metrics = [] + + dashboard_hash[:panel_groups].each do |panel_group| + panel_group[:panels].each do |panel| + panel[:metrics].each do |metric| + prometheus_metrics << { + project: project, + title: panel[:title], + y_label: panel[:y_label], + query: metric[:query_range] || metric[:query], + unit: metric[:unit], + legend: metric[:label], + identifier: metric[:id], + group: Enums::PrometheusMetric.groups[:custom], + common: false, + dashboard_path: dashboard_path + }.compact + end + end + end + + prometheus_metrics + end + + private + + attr_reader :dashboard_hash, :project, :dashboard_path + end + end + end + end + end + end +end diff --git a/lib/gitlab/usage_data.rb b/lib/gitlab/usage_data.rb index 1dd6eab1743..a9e2ba4d85c 100644 --- a/lib/gitlab/usage_data.rb +++ b/lib/gitlab/usage_data.rb @@ -269,6 +269,9 @@ module Gitlab database: { adapter: alt_usage_data { Gitlab::Database.adapter_name }, version: alt_usage_data { Gitlab::Database.version } + }, + mail: { + smtp_server: alt_usage_data { ActionMailer::Base.smtp_settings[:address] } } } end diff --git a/lib/mattermost/session.rb b/lib/mattermost/session.rb index eea7daa3d8e..ccdd1443fb0 100644 --- a/lib/mattermost/session.rb +++ b/lib/mattermost/session.rb @@ -52,7 +52,7 @@ module Mattermost # Next methods are needed for Doorkeeper def pre_auth @pre_auth ||= Doorkeeper::OAuth::PreAuthorization.new( - Doorkeeper.configuration, server.client_via_uid, params) + Doorkeeper.configuration, params) end def authorization diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake index 42c8adbfed1..70d22837335 100644 --- a/lib/tasks/gitlab/backup.rake +++ b/lib/tasks/gitlab/backup.rake @@ -278,5 +278,7 @@ namespace :gitlab do $stdout end end - end # namespace end: backup -end # namespace end: gitlab + end + # namespace end: backup +end +# namespace end: gitlab diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 469e959ad54..17aed3bf769 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -7426,6 +7426,9 @@ msgstr "" msgid "Custom hostname (for private commit emails)" msgstr "" +msgid "Custom metrics" +msgstr "" + msgid "Custom notification events" msgstr "" @@ -26892,9 +26895,6 @@ msgstr "" msgid "Use %{code_start}::%{code_end} to create a %{link_start}scoped label set%{link_end} (eg. %{code_start}priority::1%{code_end})" msgstr "" -msgid "Use %{native_redirect_uri} for local tests" -msgstr "" - msgid "Use Service Desk to connect with your users (e.g. to offer customer support) through email right inside GitLab" msgstr "" diff --git a/package.json b/package.json index 70c88e901f1..926564977ab 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "apollo-upload-client": "^13.0.0", "autosize": "^4.0.2", "aws-sdk": "^2.637.0", - "axios": "^0.19.0", + "axios": "^0.20.0", "babel-loader": "^8.0.6", "babel-plugin-lodash": "^3.3.4", "bootstrap": "4.4.1", diff --git a/qa/Dockerfile b/qa/Dockerfile index 6310e4b290d..2b7c4a19122 100644 --- a/qa/Dockerfile +++ b/qa/Dockerfile @@ -3,8 +3,8 @@ LABEL maintainer="GitLab Quality Department " ENV DEBIAN_FRONTEND="noninteractive" ENV DOCKER_VERSION="17.09.0-ce" -ENV CHROME_VERSION="84.0.4147.89-1" -ENV CHROME_DRIVER_VERSION="84.0.4147.30" +ENV CHROME_VERSION="85.0.4183.83-1" +ENV CHROME_DRIVER_VERSION="85.0.4183.87" ENV CHROME_DEB="google-chrome-stable_${CHROME_VERSION}_amd64.deb" ENV CHROME_URL="https://s3.amazonaws.com/gitlab-google-chrome-stable/${CHROME_DEB}" diff --git a/rubocop/rubocop-usage-data.yml b/rubocop/rubocop-usage-data.yml index 34f838e4692..bcdb0631090 100644 --- a/rubocop/rubocop-usage-data.yml +++ b/rubocop/rubocop-usage-data.yml @@ -4,6 +4,7 @@ UsageData/LargeTable: - 'lib/gitlab/usage_data.rb' - 'ee/lib/ee/gitlab/usage_data.rb' NonRelatedClasses: + - :ActionMailer::Base - :Date - :Feature - :Gitlab diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh index 59189c94cde..862c3b4bb62 100755 --- a/scripts/review_apps/review-apps.sh +++ b/scripts/review_apps/review-apps.sh @@ -147,13 +147,12 @@ function disable_sign_ups() { run_task "${ruby_cmd}" # Disable sign-ups - retry 'curl --silent --show-error --request PUT --header "PRIVATE-TOKEN: ${REVIEW_APPS_ROOT_TOKEN}" "${CI_ENVIRONMENT_URL}/api/v4/application/settings?signup_enabled=false"' + local signup_enabled=$(retry 'curl --silent --show-error --request PUT --header "PRIVATE-TOKEN: ${REVIEW_APPS_ROOT_TOKEN}" "${CI_ENVIRONMENT_URL}/api/v4/application/settings?signup_enabled=false" | jq ".signup_enabled"') - local signup_enabled=$(retry 'curl --silent --show-error --request GET --header "PRIVATE-TOKEN: ${REVIEW_APPS_ROOT_TOKEN}" "${CI_ENVIRONMENT_URL}/api/v4/application/settings" | jq ".signup_enabled"') if [[ "${signup_enabled}" == "false" ]]; then echoinfo "Sign-ups have been disabled successfully." else - echoerr "Sign-ups should be disabled but are still enabled!" + echoerr "Sign-ups are still enabled!" false fi } diff --git a/spec/frontend/boards/stores/getters_spec.js b/spec/frontend/boards/stores/getters_spec.js index 1e09ad6c7c7..267451845ba 100644 --- a/spec/frontend/boards/stores/getters_spec.js +++ b/spec/frontend/boards/stores/getters_spec.js @@ -91,4 +91,28 @@ describe('Boards - Getters', () => { }); }); }); + + describe('getIssueById', () => { + const state = { issues: { '1': 'issue' } }; + + it.each` + id | expected + ${'1'} | ${'issue'} + ${''} | ${{}} + `('returns $expected when $id is passed to state', ({ id, expected }) => { + expect(getters.getIssueById(state)(id)).toEqual(expected); + }); + }); + + describe('getActiveIssue', () => { + it.each` + id | expected + ${'1'} | ${'issue'} + ${''} | ${{}} + `('returns $expected when $id is passed to state', ({ id, expected }) => { + const state = { issues: { '1': 'issue' }, activeId: id }; + + expect(getters.getActiveIssue(state)).toEqual(expected); + }); + }); }); diff --git a/spec/frontend/boards/stores/mutations_spec.js b/spec/frontend/boards/stores/mutations_spec.js index 83350b0c0b7..d1e2c49428f 100644 --- a/spec/frontend/boards/stores/mutations_spec.js +++ b/spec/frontend/boards/stores/mutations_spec.js @@ -129,19 +129,24 @@ describe('Board Store Mutations', () => { describe('RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS', () => { it('sets isLoadingIssues to false and updates issuesByListId object', () => { const listIssues = { - '1': [mockIssue], + '1': [mockIssue.id], + }; + const issues = { + '1': mockIssue, }; state = { ...state, isLoadingIssues: true, issuesByListId: {}, + issues: {}, }; - mutations.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS(state, listIssues); + mutations.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS(state, { listData: listIssues, issues }); expect(state.isLoadingIssues).toBe(false); expect(state.issuesByListId).toEqual(listIssues); + expect(state.issues).toEqual(issues); }); }); diff --git a/spec/frontend/issue_show/components/app_spec.js b/spec/frontend/issue_show/components/app_spec.js index 698f7dbb1e4..69f1d5aeec2 100644 --- a/spec/frontend/issue_show/components/app_spec.js +++ b/spec/frontend/issue_show/components/app_spec.js @@ -1,14 +1,19 @@ import { GlIntersectionObserver } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; -import { TEST_HOST } from 'helpers/test_constants'; import { useMockIntersectionObserver } from 'helpers/mock_dom_observer'; import axios from '~/lib/utils/axios_utils'; import { visitUrl } from '~/lib/utils/url_utility'; import '~/behaviors/markdown/render_gfm'; import IssuableApp from '~/issue_show/components/app.vue'; import eventHub from '~/issue_show/event_hub'; -import { initialRequest, secondRequest } from '../mock_data'; +import { + appProps, + initialRequest, + publishedIncidentUrl, + secondRequest, + zoomMeetingUrl, +} from '../mock_data'; import IncidentTabs from '~/issue_show/components/incident_tabs.vue'; import DescriptionComponent from '~/issue_show/components/description.vue'; import PinnedLinks from '~/issue_show/components/pinned_links.vue'; @@ -22,30 +27,6 @@ jest.mock('~/issue_show/event_hub'); const REALTIME_REQUEST_STACK = [initialRequest, secondRequest]; -const zoomMeetingUrl = 'https://gitlab.zoom.us/j/95919234811'; -const publishedIncidentUrl = 'https://status.com/'; - -const defaultProps = { - canUpdate: true, - canDestroy: true, - endpoint: '/gitlab-org/gitlab-shell/-/issues/9/realtime_changes', - updateEndpoint: TEST_HOST, - issuableRef: '#1', - issuableStatus: 'opened', - initialTitleHtml: '', - initialTitleText: '', - initialDescriptionHtml: 'test', - initialDescriptionText: 'test', - lockVersion: 1, - markdownPreviewPath: '/', - markdownDocsPath: '/', - projectNamespace: '/', - projectPath: '/', - issuableTemplateNamesPath: '/issuable-templates-path', - zoomMeetingUrl, - publishedIncidentUrl, -}; - describe('Issuable output', () => { useMockIntersectionObserver(); @@ -57,7 +38,7 @@ describe('Issuable output', () => { const mountComponent = (props = {}) => { wrapper = mount(IssuableApp, { - propsData: { ...defaultProps, ...props }, + propsData: { ...appProps, ...props }, }); }; diff --git a/spec/frontend/issue_show/issue_spec.js b/spec/frontend/issue_show/issue_spec.js index 29b4f49f74f..befb670c6cd 100644 --- a/spec/frontend/issue_show/issue_spec.js +++ b/spec/frontend/issue_show/issue_spec.js @@ -1,25 +1,44 @@ +import MockAdapter from 'axios-mock-adapter'; +import { useMockIntersectionObserver } from 'helpers/mock_dom_observer'; +import waitForPromises from 'helpers/wait_for_promises'; +import axios from '~/lib/utils/axios_utils'; import initIssuableApp from '~/issue_show/issue'; -import { parseIssuableData } from '~/issue_show/utils/parse_data'; +import * as parseData from '~/issue_show/utils/parse_data'; +import { appProps } from './mock_data'; + +const mock = new MockAdapter(axios); +mock.onGet().reply(200); + +useMockIntersectionObserver(); + +jest.mock('~/lib/utils/poll'); + +const setupHTML = initialData => { + document.body.innerHTML = ` +
+ + `; +}; describe('Issue show index', () => { describe('initIssueableApp', () => { - // Warning: this test is currently faulty. - // More details at https://gitlab.com/gitlab-org/gitlab/-/issues/241717 - // eslint-disable-next-line jest/no-disabled-tests - it.skip('should initialize app with no potential XSS attack', () => { - const d = document.createElement('div'); - d.id = 'js-issuable-app-initial-data'; + it('should initialize app with no potential XSS attack', async () => { + const alertSpy = jest.spyOn(window, 'alert').mockImplementation(() => {}); + const parseDataSpy = jest.spyOn(parseData, 'parseIssuableData'); - d.innerHTML = JSON.stringify({ - initialDescriptionHtml: '<img src=x onerror=alert(1)>', + setupHTML({ + ...appProps, + initialDescriptionHtml: '', }); - document.body.appendChild(d); - - const alertSpy = jest.spyOn(window, 'alert'); - const issuableData = parseIssuableData(); + const issuableData = parseData.parseIssuableData(); initIssuableApp(issuableData); + await waitForPromises(); + + expect(parseDataSpy).toHaveBeenCalled(); expect(alertSpy).not.toHaveBeenCalled(); }); }); diff --git a/spec/frontend/issue_show/mock_data.js b/spec/frontend/issue_show/mock_data.js index 6d17eb08bf7..5a31a550088 100644 --- a/spec/frontend/issue_show/mock_data.js +++ b/spec/frontend/issue_show/mock_data.js @@ -31,3 +31,28 @@ export const descriptionProps = { taskStatus: '', updateUrl: TEST_HOST, }; + +export const publishedIncidentUrl = 'https://status.com/'; + +export const zoomMeetingUrl = 'https://gitlab.zoom.us/j/95919234811'; + +export const appProps = { + canUpdate: true, + canDestroy: true, + endpoint: '/gitlab-org/gitlab-shell/-/issues/9/realtime_changes', + updateEndpoint: TEST_HOST, + issuableRef: '#1', + issuableStatus: 'opened', + initialTitleHtml: '', + initialTitleText: '', + initialDescriptionHtml: 'test', + initialDescriptionText: 'test', + lockVersion: 1, + markdownPreviewPath: '/', + markdownDocsPath: '/', + projectNamespace: '/', + projectPath: '/', + issuableTemplateNamesPath: '/issuable-templates-path', + zoomMeetingUrl, + publishedIncidentUrl, +}; diff --git a/spec/lib/gitlab/metrics/dashboard/importer_spec.rb b/spec/lib/gitlab/metrics/dashboard/importer_spec.rb new file mode 100644 index 00000000000..8b705395a2c --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/importer_spec.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Dashboard::Importer do + include MetricsDashboardHelpers + + let_it_be(:dashboard_path) { '.gitlab/dashboards/sample_dashboard.yml' } + let_it_be(:project) { create(:project) } + + before do + allow(subject).to receive(:dashboard_hash).and_return(dashboard_hash) + end + + subject { described_class.new(dashboard_path, project) } + + describe '.execute' do + context 'valid dashboard hash' do + let(:dashboard_hash) { load_sample_dashboard } + + it 'imports metrics to database' do + expect { subject.execute } + .to change { PrometheusMetric.count }.from(0).to(3) + end + end + + context 'invalid dashboard hash' do + let(:dashboard_hash) { {} } + + it 'returns false' do + expect(subject.execute).to be(false) + end + end + end + + describe '.execute!' do + context 'valid dashboard hash' do + let(:dashboard_hash) { load_sample_dashboard } + + it 'imports metrics to database' do + expect { subject.execute } + .to change { PrometheusMetric.count }.from(0).to(3) + end + end + + context 'invalid dashboard hash' do + let(:dashboard_hash) { {} } + + it 'raises error' do + expect { subject.execute! }.to raise_error(Gitlab::Metrics::Dashboard::Validator::Errors::SchemaValidationError, + 'root is missing required keys: dashboard, panel_groups') + end + end + end +end diff --git a/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb new file mode 100644 index 00000000000..09d5e048f6a --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/importers/prometheus_metrics_spec.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Dashboard::Importers::PrometheusMetrics do + include MetricsDashboardHelpers + + describe '#execute' do + let(:project) { create(:project) } + let(:dashboard_path) { 'path/to/dashboard.yml' } + + subject { described_class.new(dashboard_hash, project: project, dashboard_path: dashboard_path) } + + context 'valid dashboard' do + let(:dashboard_hash) { load_sample_dashboard } + + context 'with all new metrics' do + it 'creates PrometheusMetrics' do + expect { subject.execute }.to change { PrometheusMetric.count }.by(3) + end + end + + context 'with existing metrics' do + let!(:existing_metric) do + create(:prometheus_metric, { + project: project, + identifier: 'metric_b', + title: 'overwrite', + y_label: 'overwrite', + query: 'overwrite', + unit: 'overwrite', + legend: 'overwrite' + }) + end + + it 'updates existing PrometheusMetrics' do + described_class.new(dashboard_hash, project: project, dashboard_path: dashboard_path).execute + + expect(existing_metric.reload.attributes.with_indifferent_access).to include({ + title: 'Super Chart B', + y_label: 'y_label', + query: 'query', + unit: 'unit', + legend: 'Legend Label' + }) + end + + it 'creates new PrometheusMetrics' do + expect { subject.execute }.to change { PrometheusMetric.count }.by(2) + end + + context 'with stale metrics' do + let!(:stale_metric) do + create(:prometheus_metric, + project: project, + identifier: 'stale_metric', + dashboard_path: dashboard_path, + group: 3 + ) + end + + it 'deletes stale metrics' do + subject.execute + + expect { stale_metric.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + end + end + + context 'invalid dashboard' do + let(:dashboard_hash) { {} } + + it 'returns false' do + expect(subject.execute).to eq(false) + end + end + end +end diff --git a/spec/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics_spec.rb b/spec/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics_spec.rb new file mode 100644 index 00000000000..3af8b51c889 --- /dev/null +++ b/spec/lib/gitlab/metrics/dashboard/transformers/yml/v1/prometheus_metrics_spec.rb @@ -0,0 +1,99 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Metrics::Dashboard::Transformers::Yml::V1::PrometheusMetrics do + include MetricsDashboardHelpers + + describe '#execute' do + subject { described_class.new(dashboard_hash) } + + context 'valid dashboard' do + let_it_be(:dashboard_hash) do + { + panel_groups: [{ + panels: [ + { + title: 'Panel 1 title', + y_label: 'Panel 1 y_label', + metrics: [ + { + query_range: 'Panel 1 metric 1 query_range', + unit: 'Panel 1 metric 1 unit', + label: 'Panel 1 metric 1 label', + id: 'Panel 1 metric 1 id' + }, + { + query: 'Panel 1 metric 2 query', + unit: 'Panel 1 metric 2 unit', + label: 'Panel 1 metric 2 label', + id: 'Panel 1 metric 2 id' + } + ] + }, + { + title: 'Panel 2 title', + y_label: 'Panel 2 y_label', + metrics: [{ + query_range: 'Panel 2 metric 1 query_range', + unit: 'Panel 2 metric 1 unit', + label: 'Panel 2 metric 1 label', + id: 'Panel 2 metric 1 id' + }] + } + ] + }] + } + end + + let(:expected_metrics) do + [ + { + title: 'Panel 1 title', + y_label: 'Panel 1 y_label', + query: "Panel 1 metric 1 query_range", + unit: 'Panel 1 metric 1 unit', + legend: 'Panel 1 metric 1 label', + identifier: 'Panel 1 metric 1 id', + group: 3, + common: false + }, + { + title: 'Panel 1 title', + y_label: 'Panel 1 y_label', + query: 'Panel 1 metric 2 query', + unit: 'Panel 1 metric 2 unit', + legend: 'Panel 1 metric 2 label', + identifier: 'Panel 1 metric 2 id', + group: 3, + common: false + }, + { + title: 'Panel 2 title', + y_label: 'Panel 2 y_label', + query: 'Panel 2 metric 1 query_range', + unit: 'Panel 2 metric 1 unit', + legend: 'Panel 2 metric 1 label', + identifier: 'Panel 2 metric 1 id', + group: 3, + common: false + } + ] + end + + it 'returns collection of metrics with correct attributes' do + expect(subject.execute).to match_array(expected_metrics) + end + end + + context 'invalid dashboard' do + let(:dashboard_hash) { {} } + + it 'raises missing attribute error' do + expect { subject.execute }.to raise_error( + ::Gitlab::Metrics::Dashboard::Transformers::Errors::MissingAttribute, "Missing attribute: 'panel_groups'" + ) + end + end + end +end diff --git a/spec/lib/gitlab/usage_data_spec.rb b/spec/lib/gitlab/usage_data_spec.rb index 2a51a9e3410..dc7bfc6e870 100644 --- a/spec/lib/gitlab/usage_data_spec.rb +++ b/spec/lib/gitlab/usage_data_spec.rb @@ -673,6 +673,7 @@ RSpec.describe Gitlab::UsageData, :aggregate_failures do expect(subject[:git][:version]).to eq(Gitlab::Git.version) expect(subject[:database][:adapter]).to eq(Gitlab::Database.adapter_name) expect(subject[:database][:version]).to eq(Gitlab::Database.version) + expect(subject[:mail][:smtp_server]).to eq(ActionMailer::Base.smtp_settings[:address]) expect(subject[:gitaly][:version]).to be_present expect(subject[:gitaly][:servers]).to be >= 1 expect(subject[:gitaly][:clusters]).to be >= 0 diff --git a/spec/migrations/ensure_target_project_id_is_filled_spec.rb b/spec/migrations/ensure_target_project_id_is_filled_spec.rb new file mode 100644 index 00000000000..72d59a72814 --- /dev/null +++ b/spec/migrations/ensure_target_project_id_is_filled_spec.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require 'spec_helper' +require Rails.root.join('db', 'post_migrate', '20200831065705_ensure_target_project_id_is_filled.rb') + +RSpec.describe EnsureTargetProjectIdIsFilled, schema: 20200827085101 do + let_it_be(:namespaces) { table(:namespaces) } + let_it_be(:projects) { table(:projects) } + let_it_be(:merge_requests) { table(:merge_requests) } + let_it_be(:metrics) { table(:merge_request_metrics) } + + let!(:namespace) { namespaces.create!(name: 'namespace', path: 'namespace') } + let!(:project_1) { projects.create!(namespace_id: namespace.id) } + let!(:project_2) { projects.create!(namespace_id: namespace.id) } + let!(:merge_request_to_migrate_1) { merge_requests.create!(source_branch: 'a', target_branch: 'b', target_project_id: project_1.id) } + let!(:merge_request_to_migrate_2) { merge_requests.create!(source_branch: 'c', target_branch: 'd', target_project_id: project_2.id) } + let!(:merge_request_not_to_migrate) { merge_requests.create!(source_branch: 'e', target_branch: 'f', target_project_id: project_1.id) } + + let!(:metrics_1) { metrics.create!(merge_request_id: merge_request_to_migrate_1.id) } + let!(:metrics_2) { metrics.create!(merge_request_id: merge_request_to_migrate_2.id) } + let!(:metrics_3) { metrics.create!(merge_request_id: merge_request_not_to_migrate.id, target_project_id: project_1.id) } + + it 'migrates missing target_project_ids' do + migrate! + + expect(metrics_1.reload.target_project_id).to eq(project_1.id) + expect(metrics_2.reload.target_project_id).to eq(project_2.id) + expect(metrics_3.reload.target_project_id).to eq(project_1.id) + end +end diff --git a/spec/models/merge_request/metrics_spec.rb b/spec/models/merge_request/metrics_spec.rb index 82402b95597..760eaf1ac7f 100644 --- a/spec/models/merge_request/metrics_spec.rb +++ b/spec/models/merge_request/metrics_spec.rb @@ -9,17 +9,6 @@ RSpec.describe MergeRequest::Metrics do it { is_expected.to belong_to(:merged_by).class_name('User') } end - it 'sets `target_project_id` before save' do - merge_request = create(:merge_request) - metrics = merge_request.metrics - - metrics.update_column(:target_project_id, nil) - - metrics.save! - - expect(metrics.target_project_id).to eq(merge_request.target_project_id) - end - describe 'scopes' do let_it_be(:metrics_1) { create(:merge_request).metrics.tap { |m| m.update!(merged_at: 10.days.ago) } } let_it_be(:metrics_2) { create(:merge_request).metrics.tap { |m| m.update!(merged_at: 5.days.ago) } } diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 63c2b6fbf7b..c9389ad7f43 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -304,18 +304,6 @@ RSpec.describe MergeRequest, factory_default: :keep do expect(merge_request.target_project_id).to eq(project.id) expect(merge_request.target_project_id).to eq(merge_request.metrics.target_project_id) end - - context 'when metrics record already exists with NULL target_project_id' do - before do - merge_request.metrics.update_column(:target_project_id, nil) - end - - it 'returns the metrics record' do - metrics_record = merge_request.ensure_metrics - - expect(metrics_record).to be_persisted - end - end end end diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb index 6ac46712aa3..a2cc2b12e5e 100644 --- a/spec/tasks/gitlab/backup_rake_spec.rb +++ b/spec/tasks/gitlab/backup_rake_spec.rb @@ -160,7 +160,8 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do expect(raw_repo.empty?).to be(false) end end - end # backup_restore task + end + # backup_restore task describe 'backup' do before do @@ -391,7 +392,8 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do expect { run_rake_task('gitlab:backup:create') }.to output.to_stdout end end - end # backup_create task + end + # backup_create task describe "Skipping items" do before do @@ -486,4 +488,5 @@ RSpec.describe 'gitlab:app namespace rake task', :delete do expect(backup_tar).to match(/\d+_\d{4}_\d{2}_\d{2}_\d+\.\d+\.\d+.*_gitlab_backup.tar$/) end end -end # gitlab:app namespace +end +# gitlab:app namespace diff --git a/yarn.lock b/yarn.lock index 50f38b57dc6..1586814bdb2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2085,13 +2085,12 @@ axios-mock-adapter@^1.15.0: dependencies: deep-equal "^1.0.1" -axios@^0.19.0: - version "0.19.0" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.19.0.tgz#8e09bff3d9122e133f7b8101c8fbdd00ed3d2ab8" - integrity sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ== +axios@^0.20.0: + version "0.20.0" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.20.0.tgz#057ba30f04884694993a8cd07fa394cff11c50bd" + integrity sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA== dependencies: - follow-redirects "1.5.10" - is-buffer "^2.0.2" + follow-redirects "^1.10.0" babel-eslint@^10.0.3: version "10.0.3" @@ -5139,13 +5138,18 @@ flush-write-stream@^1.0.0: inherits "^2.0.3" readable-stream "^2.3.6" -follow-redirects@1.5.10, follow-redirects@^1.0.0: +follow-redirects@^1.0.0: version "1.5.10" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a" integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ== dependencies: debug "=3.1.0" +follow-redirects@^1.10.0: + version "1.13.0" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.0.tgz#b42e8d93a2a7eea5ed88633676d6597bc8e384db" + integrity sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA== + font-awesome@4.7.0: version "4.7.0" resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" @@ -6194,7 +6198,7 @@ is-buffer@^1.1.5, is-buffer@~1.1.1: resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== -is-buffer@^2.0.0, is-buffer@^2.0.2: +is-buffer@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725" integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==