diff options
32 files changed, 535 insertions, 283 deletions
diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue index 10a30bd44b1..4d1e550d13e 100644 --- a/app/assets/javascripts/repository/components/table/index.vue +++ b/app/assets/javascripts/repository/components/table/index.vue @@ -87,6 +87,7 @@ export default { <table :aria-label="tableCaption" class="table tree-table" + :class="{ 'gl-table-layout-fixed': !showParentRow }" aria-live="polite" data-qa-selector="file_tree_table" > @@ -120,7 +121,9 @@ export default { <template v-if="isLoading"> <tr v-for="i in 5" :key="i" aria-hidden="true"> <td><gl-skeleton-loading :lines="1" class="h-auto" /></td> - <td><gl-skeleton-loading :lines="1" class="h-auto" /></td> + <td class="gl-display-none gl-sm-display-block"> + <gl-skeleton-loading :lines="1" class="h-auto" /> + </td> <td><gl-skeleton-loading :lines="1" class="ml-auto h-auto w-50" /></td> </tr> </template> diff --git a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue index 24f1c9c5bd0..5d8541cf5e8 100644 --- a/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue +++ b/app/assets/javascripts/vue_merge_request_widget/components/extensions/base.vue @@ -1,5 +1,13 @@ <script> -import { GlButton, GlLoadingIcon, GlLink, GlBadge, GlSafeHtmlDirective } from '@gitlab/ui'; +import { + GlButton, + GlLoadingIcon, + GlLink, + GlBadge, + GlSafeHtmlDirective, + GlTooltipDirective, +} from '@gitlab/ui'; +import { sprintf, s__ } from '~/locale'; import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue'; import { EXTENSION_ICON_CLASS } from '../../constants'; import StatusIcon from './status_icon.vue'; @@ -21,6 +29,7 @@ export default { }, directives: { SafeHtml: GlSafeHtmlDirective, + GlTooltip: GlTooltipDirective, }, data() { return { @@ -44,6 +53,14 @@ export default { return true; }, + collapseButtonLabel() { + return sprintf( + this.isCollapsed + ? s__('mrWidget|Show %{widget} details') + : s__('mrWidget|Hide %{widget} details'), + { widget: this.$options.name }, + ); + }, statusIconName() { if (this.isLoadingSummary) return null; @@ -102,22 +119,28 @@ export default { :is-loading="isLoadingSummary" :icon-name="statusIconName" /> - <div class="media-body d-flex flex-align-self-center align-items-center"> + <div class="media-body gl-display-flex gl-align-self-center gl-align-items-center"> <div class="code-text"> <template v-if="isLoadingSummary"> {{ __('Loading...') }} </template> <div v-else v-safe-html="summary(collapsedData)"></div> </div> - <gl-button - v-if="isCollapsible" - size="small" - class="float-right align-self-center" - data-testid="toggle-button" - @click="toggleCollapsed" + <div + class="gl-float-right gl-align-self-center gl-border-l-1 gl-border-l-solid gl-border-gray-100 gl-ml-3 gl-pl-3" > - {{ isCollapsed ? __('Expand') : __('Collapse') }} - </gl-button> + <gl-button + v-if="isCollapsible" + v-gl-tooltip + :title="collapseButtonLabel" + :aria-expanded="`${!isCollapsed}`" + :aria-label="collapseButtonLabel" + :icon="isCollapsed ? 'chevron-lg-down' : 'chevron-lg-up'" + category="tertiary" + data-testid="toggle-button" + @click="toggleCollapsed" + /> + </div> </div> </div> <div @@ -137,11 +160,9 @@ export default { wclass="report-block-list" class="report-block-container" > - <li v-for="data in fullData" :key="data.id" class="d-flex align-items-center"> + <li v-for="data in fullData" :key="data.id" class="gl-display-flex gl-align-items-center"> <status-icon v-if="data.icon" :icon-name="data.icon.name" :size="12" /> - <div - class="gl-mt-2 gl-mb-2 align-content-around align-items-start flex-wrap align-self-center d-flex" - > + <div class="gl-mt-2 gl-mb-2 gl-flex-wrap gl-align-self-center gl-display-flex"> <div v-safe-html="data.text" class="gl-mr-4"></div> <div v-if="data.link"> <gl-link :href="data.link.href">{{ data.link.text }}</gl-link> diff --git a/app/assets/stylesheets/application_dark.scss b/app/assets/stylesheets/application_dark.scss index dae0cd72a8f..f1d7df8c5ed 100644 --- a/app/assets/stylesheets/application_dark.scss +++ b/app/assets/stylesheets/application_dark.scss @@ -2,74 +2,4 @@ @import './application'; -@import './themes/theme_helper'; - -body.gl-dark { - @include gitlab-theme( - $gray-900, - $gray-400, - $gray-500, - $gray-800, - $gray-900, - $white - ); - - .logo-text svg { - fill: var(--gl-text-color); - } - - .navbar-gitlab { - background-color: var(--gray-50); - box-shadow: 0 1px 0 0 var(--gray-100); - - .navbar-sub-nav, - .navbar-nav { - li { - > a:hover, - > a:focus, - > button:hover, - > button:focus { - color: var(--gl-text-color); - background-color: var(--gray-200); - } - } - - li.active, - li.dropdown.show { - > a, - > button { - color: var(--gl-text-color); - background-color: var(--gray-200); - } - } - } - - .header-search { - background-color: var(--gray-100) !important; - box-shadow: inset 0 0 0 1px var(--border-color) !important; - - &:active, - &:hover { - background-color: var(--gray-100) !important; - box-shadow: inset 0 0 0 1px var(--blue-200) !important; - } - } - - .search { - form { - background-color: var(--gray-100); - box-shadow: inset 0 0 0 1px var(--border-color); - - &:active, - &:hover { - background-color: var(--gray-100); - box-shadow: inset 0 0 0 1px var(--blue-200); - } - } - } - } - - .md :not(pre.code) > code { - background-color: $gray-200; - } -} +@import './themes/dark_mode_overrides'; diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 461d6a29b3a..25f5c0407f7 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -7,36 +7,8 @@ opacity: 1; } -.check-all-holder { - line-height: 36px; - float: left; - margin-right: 15px; -} - -form.edit-issue { - margin: 0; -} - -ul.related-merge-requests > li { - display: flex; - align-items: center; - - .merge-request-id { - flex-shrink: 0; - } - - .merge-request-info { - margin-left: 5px; - } - - gl-emoji { - font-size: 1em; - } -} - -.related-branches-title { - font-size: 16px; - font-weight: $gl-font-weight-bold; +ul.related-merge-requests > li gl-emoji { + font-size: 1em; } .merge-request-status { @@ -92,35 +64,12 @@ ul.related-merge-requests > li { } } -.issues-footer { - padding-top: $gl-padding; - padding-bottom: 37px; -} - -.issues-nav-controls, -.new-branch-col { - font-size: 0; -} - .issues-nav-controls { .btn-group:empty { display: none; } } -.email-modal-input-group { - margin-bottom: 10px; - - .form-control { - background-color: $white; - } - - .btn { - background-color: $gray-light; - border: 1px solid $border-gray-normal; - } -} - .recaptcha { margin-bottom: 30px; } diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss index 3e3b514dd3d..2f68694a2a5 100644 --- a/app/assets/stylesheets/startup/startup-dark.scss +++ b/app/assets/stylesheets/startup/startup-dark.scss @@ -16,9 +16,6 @@ body.gl-dark { --border-color: #4f4f4f; --black: #fff; } -.nav-sidebar li.active { - box-shadow: none; -} :root { --white: #333; } @@ -1610,6 +1607,95 @@ svg.s16 { .rect-avatar.s32 { border-radius: 4px; } +body.gl-dark { + --gray-10: #1f1f1f; + --gray-50: #303030; + --gray-100: #404040; + --gray-200: #525252; + --gray-300: #5e5e5e; + --gray-400: #868686; + --gray-500: #999; + --gray-600: #bfbfbf; + --gray-700: #dbdbdb; + --gray-800: #f0f0f0; + --gray-900: #fafafa; + --gray-950: #fff; + --green-50: #0a4020; + --green-100: #0d532a; + --green-200: #24663b; + --green-300: #217645; + --green-400: #108548; + --green-500: #2da160; + --green-600: #52b87a; + --green-700: #91d4a8; + --green-800: #c3e6cd; + --green-900: #ecf4ee; + --green-950: #f1fdf6; + --blue-50: #033464; + --blue-100: #064787; + --blue-200: #0b5cad; + --blue-300: #1068bf; + --blue-400: #1f75cb; + --blue-500: #428fdc; + --blue-600: #63a6e9; + --blue-700: #9dc7f1; + --blue-800: #cbe2f9; + --blue-900: #e9f3fc; + --blue-950: #f2f9ff; + --orange-50: #5c2900; + --orange-100: #703800; + --orange-200: #8f4700; + --orange-300: #9e5400; + --orange-400: #ab6100; + --orange-500: #c17d10; + --orange-600: #d99530; + --orange-700: #e9be74; + --orange-800: #f5d9a8; + --orange-900: #fdf1dd; + --orange-950: #fff4e1; + --red-50: #660e00; + --red-100: #8d1300; + --red-200: #ae1800; + --red-300: #c91c00; + --red-400: #dd2b0e; + --red-500: #ec5941; + --red-600: #f57f6c; + --red-700: #fcb5aa; + --red-800: #fdd4cd; + --red-900: #fcf1ef; + --red-950: #fff4f3; + --indigo-50: #1a1a40; + --indigo-100: #292961; + --indigo-200: #393982; + --indigo-300: #4b4ba3; + --indigo-400: #5b5bbd; + --indigo-500: #6666c4; + --indigo-600: #7c7ccc; + --indigo-700: #a6a6de; + --indigo-800: #d1d1f0; + --indigo-900: #ebebfa; + --indigo-950: #f7f7ff; + --indigo-900-alpha-008: rgba(235, 235, 250, 0.08); + --purple-50: #232150; + --purple-100: #2f2a6b; + --purple-200: #453894; + --purple-300: #5943b6; + --purple-400: #694cc0; + --purple-500: #7b58cf; + --purple-600: #9475db; + --purple-700: #ac93e6; + --purple-800: #cbbbf2; + --purple-900: #e1d8f9; + --purple-950: #f4f0ff; + --gl-text-color: #fafafa; + --border-color: #4f4f4f; + --white: #333; + --black: #fff; + --svg-status-bg: #333; +} +.nav-sidebar li.active { + box-shadow: none; +} body.gl-dark .navbar-gitlab { background-color: #fafafa; } @@ -1819,9 +1905,6 @@ body.gl-dark { --black: #fff; --svg-status-bg: #333; } -.nav-sidebar li.active { - box-shadow: none; -} .tab-width-8 { -moz-tab-size: 8; tab-size: 8; diff --git a/app/assets/stylesheets/themes/_dark.scss b/app/assets/stylesheets/themes/_dark.scss index 76b9e10ef1a..c79816e3579 100644 --- a/app/assets/stylesheets/themes/_dark.scss +++ b/app/assets/stylesheets/themes/_dark.scss @@ -257,53 +257,3 @@ $line-removed-dark: $red-200; $well-expand-item: $gray-200; $well-inner-border: $gray-200; - -// Misc component overrides that should live elsewhere -.gl-label { - filter: brightness(0.9) contrast(1.1); - - // This applies to the gl-label markups - // rendered and cached in the backend (labels_helper.rb) - &.gl-label-scoped { - .gl-label-text-scoped, - .gl-label-close { - color: $gray-900; - } - } -} - -// white-ish text for light labels -.gl-label-text-light.gl-label-text-light { - color: $gray-900; -} - -.gl-label-text-dark.gl-label-text-dark { - color: $gray-10; -} - -// This applies to "gl-labels" from "gitlab-ui" -.gl-label.gl-label-scoped.gl-label-text-dark, -.gl-label.gl-label-scoped.gl-label-text-light { - .gl-label-text-scoped, - .gl-label-close { - color: $gray-900; - } -} - -// duplicated class as the original .atwho-view style is added later -.atwho-view.atwho-view { - background-color: $white; - color: $gray-900; - border-color: $gray-800; -} - -.nav-sidebar { - li.active { - box-shadow: none; - } - - .sidebar-sub-level-items.fly-out-list { - box-shadow: none; - border: 1px solid $border-color; - } -} diff --git a/app/assets/stylesheets/themes/dark_mode_overrides.scss b/app/assets/stylesheets/themes/dark_mode_overrides.scss new file mode 100644 index 00000000000..b77048174c9 --- /dev/null +++ b/app/assets/stylesheets/themes/dark_mode_overrides.scss @@ -0,0 +1,116 @@ +@import './themes/dark'; +@import 'page_bundles/mixins_and_variables_and_functions'; +@import './themes/theme_helper'; + +// Some hacks and overrides for things that don't properly support dark mode +.gl-label { + filter: brightness(0.9) contrast(1.1); + + // This applies to the gl-label markups + // rendered and cached in the backend (labels_helper.rb) + &.gl-label-scoped { + .gl-label-text-scoped, + .gl-label-close { + color: $gray-900; + } + } +} + +// white-ish text for light labels +.gl-label-text-light.gl-label-text-light { + color: $gray-900; +} + +.gl-label-text-dark.gl-label-text-dark { + color: $gray-10; +} + +// This applies to "gl-labels" from "gitlab-ui" +.gl-label.gl-label-scoped.gl-label-text-dark, +.gl-label.gl-label-scoped.gl-label-text-light { + .gl-label-text-scoped, + .gl-label-close { + color: $gray-900; + } +} + +// duplicated class as the original .atwho-view style is added later +.atwho-view.atwho-view { + background-color: $white; + color: $gray-900; + border-color: $gray-800; +} + +.nav-sidebar { + li.active { + box-shadow: none; + } + + .sidebar-sub-level-items.fly-out-list { + box-shadow: none; + border: 1px solid $border-color; + } +} + +body.gl-dark { + @include gitlab-theme($gray-900, $gray-400, $gray-500, $gray-800, $gray-900, $white); + + .logo-text svg { + fill: var(--gl-text-color); + } + + .navbar-gitlab { + background-color: var(--gray-50); + box-shadow: 0 1px 0 0 var(--gray-100); + + .navbar-sub-nav, + .navbar-nav { + li { + > a:hover, + > a:focus, + > button:hover, + > button:focus { + color: var(--gl-text-color); + background-color: var(--gray-200); + } + } + + li.active, + li.dropdown.show { + > a, + > button { + color: var(--gl-text-color); + background-color: var(--gray-200); + } + } + } + + .header-search { + background-color: var(--gray-100) !important; + box-shadow: inset 0 0 0 1px var(--border-color) !important; + + &:active, + &:hover { + background-color: var(--gray-100) !important; + box-shadow: inset 0 0 0 1px var(--blue-200) !important; + } + } + + .search { + form { + background-color: var(--gray-100); + box-shadow: inset 0 0 0 1px var(--border-color); + + &:active, + &:hover { + background-color: var(--gray-100); + box-shadow: inset 0 0 0 1px var(--blue-200); + } + } + } + } + + .md :not(pre.code) > code { + background-color: $gray-200; + } +} diff --git a/app/finders/projects/members/effective_access_level_finder.rb b/app/finders/projects/members/effective_access_level_finder.rb index c1e3842a9e4..d238679f2fb 100644 --- a/app/finders/projects/members/effective_access_level_finder.rb +++ b/app/finders/projects/members/effective_access_level_finder.rb @@ -99,7 +99,7 @@ module Projects end def include_membership_from_project_group_shares? - project.allowed_to_share_with_group? && project.project_group_links.any? + !project.namespace.share_with_group_lock && project.project_group_links.any? end # methods for `select` options diff --git a/app/graphql/types/ci/runner_status_enum.rb b/app/graphql/types/ci/runner_status_enum.rb index ad69175e44a..8501ce20204 100644 --- a/app/graphql/types/ci/runner_status_enum.rb +++ b/app/graphql/types/ci/runner_status_enum.rb @@ -6,8 +6,21 @@ module Types graphql_name 'CiRunnerStatus' ::Ci::Runner::AVAILABLE_STATUSES.each do |status| + description = case status + when 'active' + "A runner that is not paused." + when 'online' + "A runner that contacted this instance within the last #{::Ci::Runner::ONLINE_CONTACT_TIMEOUT.inspect}." + when 'offline' + "A runner that has not contacted this instance within the last #{::Ci::Runner::ONLINE_CONTACT_TIMEOUT.inspect}." + when 'not_connected' + "A runner that has never contacted this instance." + else + "A runner that is #{status.to_s.tr('_', ' ')}." + end + value status.to_s.upcase, - description: "A runner that is #{status.to_s.tr('_', ' ')}.", + description: description, value: status.to_sym end end diff --git a/app/views/projects/issues/_nav_btns.html.haml b/app/views/projects/issues/_nav_btns.html.haml index ade48695ddc..8d16c3d978f 100644 --- a/app/views/projects/issues/_nav_btns.html.haml +++ b/app/views/projects/issues/_nav_btns.html.haml @@ -5,7 +5,7 @@ - can_edit = can?(current_user, :admin_project, @project) - notification_email = @current_user.present? ? @current_user.notification_email_or_default : nil -.nav-controls.issues-nav-controls +.nav-controls.issues-nav-controls.gl-font-size-0 - if show_feed_buttons = render 'shared/issuable/feed_buttons' @@ -18,4 +18,3 @@ issue: { milestone_id: finder.milestones.first.try(:id) }), class: "gl-button btn btn-confirm", id: "new_issue_link" - diff --git a/app/views/projects/issues/_related_branches.html.haml b/app/views/projects/issues/_related_branches.html.haml index 0604e89be6e..c47257eec4a 100644 --- a/app/views/projects/issues/_related_branches.html.haml +++ b/app/views/projects/issues/_related_branches.html.haml @@ -1,9 +1,9 @@ - if @related_branches.any? - %h2.related-branches-title + %h2.gl-font-lg = pluralize(@related_branches.size, 'Related Branch') %ul.unstyled-list.related-merge-requests - @related_branches.each do |branch| - %li + %li.gl-display-flex.gl-align-items-center - if branch[:pipeline_status].present? %span.related-branch-ci-status = render 'ci/status/icon', status: branch[:pipeline_status] diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index e6c4b3f4814..81a7581d392 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -19,7 +19,7 @@ - if params[:search].present? = hidden_field_tag :search, params[:search] - if @can_bulk_update - .check-all-holder.d-none.d-sm-block.hidden + .check-all-holder.gl-display-none.gl-sm-display-block.hidden.gl-float-left.gl-mr-5.gl-line-height-36 - checkbox_id = 'check-all-issues' %label.gl-sr-only{ for: checkbox_id }= _('Select all') = check_box_tag checkbox_id, nil, false, class: "check-all-issues left" diff --git a/app/views/shared/issue_type/_emoji_block.html.haml b/app/views/shared/issue_type/_emoji_block.html.haml index 26d30341999..d2c851a4e49 100644 --- a/app/views/shared/issue_type/_emoji_block.html.haml +++ b/app/views/shared/issue_type/_emoji_block.html.haml @@ -4,7 +4,7 @@ .row.gl-m-0.gl-justify-content-space-between .js-noteable-awards = render 'award_emoji/awards_block', awardable: issuable, inline: true, api_awards_path: api_awards_path - .new-branch-col.gl-my-2 + .new-branch-col.gl-my-2.gl-font-size-0 = render_if_exists "projects/issues/timeline_toggle", issuable: issuable #js-vue-sort-issue-discussions #js-vue-discussion-filter{ data: { default_filter: current_user&.notes_filter_for(issuable), notes_filters: UserPreference.notes_filters.to_json } } diff --git a/app/workers/database/drop_detached_partitions_worker.rb b/app/workers/database/drop_detached_partitions_worker.rb index f9c8ce57a36..1e4dc20a0d2 100644 --- a/app/workers/database/drop_detached_partitions_worker.rb +++ b/app/workers/database/drop_detached_partitions_worker.rb @@ -10,7 +10,7 @@ module Database idempotent! def perform - Gitlab::Database::Partitioning::DetachedPartitionDropper.new.perform + Gitlab::Database::Partitioning.drop_detached_partitions ensure Gitlab::Database::Partitioning::PartitionMonitoring.new.report_metrics end diff --git a/config/feature_flags/ops/member_destroy_async_auth_refresh.yml b/config/feature_flags/ops/member_destroy_async_auth_refresh.yml index 1485b861756..71763f207db 100644 --- a/config/feature_flags/ops/member_destroy_async_auth_refresh.yml +++ b/config/feature_flags/ops/member_destroy_async_auth_refresh.yml @@ -1,7 +1,7 @@ --- name: member_destroy_async_auth_refresh introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/66424 -rollout_issue_url: +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/342120 milestone: '14.4' type: ops group: group::access diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index b180df9c6f3..16b3ce2f941 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -15382,10 +15382,10 @@ Values for sorting runners. | Value | Description | | ----- | ----------- | -| <a id="cirunnerstatusactive"></a>`ACTIVE` | A runner that is active. | -| <a id="cirunnerstatusnot_connected"></a>`NOT_CONNECTED` | A runner that is not connected. | -| <a id="cirunnerstatusoffline"></a>`OFFLINE` | A runner that is offline. | -| <a id="cirunnerstatusonline"></a>`ONLINE` | A runner that is online. | +| <a id="cirunnerstatusactive"></a>`ACTIVE` | A runner that is not paused. | +| <a id="cirunnerstatusnot_connected"></a>`NOT_CONNECTED` | A runner that has never contacted this instance. | +| <a id="cirunnerstatusoffline"></a>`OFFLINE` | A runner that has not contacted this instance within the last 2 hours. | +| <a id="cirunnerstatusonline"></a>`ONLINE` | A runner that contacted this instance within the last 2 hours. | | <a id="cirunnerstatuspaused"></a>`PAUSED` | A runner that is paused. | ### `CiRunnerType` diff --git a/doc/api/integrations.md b/doc/api/integrations.md index 75e5047f395..fedfeef19b5 100644 --- a/doc/api/integrations.md +++ b/doc/api/integrations.md @@ -300,6 +300,44 @@ Get Campfire integration settings for a project. GET /projects/:id/integrations/campfire ``` +## Datadog + +Datadog system monitoring. + +### Create/Edit Datadog service + +Set Datadog service for a project. + +```plaintext +PUT /projects/:id/services/datadog +``` + +Parameters: + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `api_key` | string | true | API key used for authentication with Datadog | +| `api_url` | string | false | (Advanced) Define the full URL for your Datadog site directly | +| `datadog_site` | string | false | Choose the Datadog site to send data to. Set to `datadoghq.eu` to send data to the EU site | +| `datadog_service` | string | false | Name of this GitLab instance that all data will be tagged with | +| `datadog_env` | string | false | The environment tag that traces will be tagged with | + +### Delete Datadog service + +Delete Datadog service for a project. + +```plaintext +DELETE /projects/:id/services/datadog +``` + +### Get Datadog service settings + +Get Datadog service settings for a project. + +```plaintext +GET /projects/:id/services/datadog +``` + ## Unify Circuit Unify Circuit RTC and collaboration tool. @@ -507,6 +545,42 @@ Get Emails on Push integration settings for a project. GET /projects/:id/integrations/emails-on-push ``` +## Engineering Workflow Management (EWM) + +Use IBM Engineering Workflow Management (EWM) as a project's issue tracker. + +### Create/Edit EWM service + +Set EWM service for a project. + +```plaintext +PUT /projects/:id/services/ewm +``` + +Parameters: + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- | ----------- | +| `new_issue_url` | string | true | The URL to create an issue in EWM | +| `project_url` | string | true | The URL to the project in EWM | +| `issues_url` | string | true | The URL to view an issue in EWM. Must contain `:id` | + +### Delete EWM service + +Delete EWM service for a project. + +```plaintext +DELETE /projects/:id/services/ewm +``` + +### Get EWM service settings + +Get EWM service settings for a project. + +```plaintext +GET /projects/:id/services/ewm +``` + ## Confluence integration > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/220934) in GitLab 13.2. diff --git a/lib/gitlab/database/partitioning.rb b/lib/gitlab/database/partitioning.rb index bbde2063c41..71fb995577a 100644 --- a/lib/gitlab/database/partitioning.rb +++ b/lib/gitlab/database/partitioning.rb @@ -14,6 +14,10 @@ module Gitlab def self.sync_partitions(models_to_sync = registered_models) MultiDatabasePartitionManager.new(models_to_sync).sync_partitions end + + def self.drop_detached_partitions + MultiDatabasePartitionDropper.new.drop_detached_partitions + end end end end diff --git a/lib/gitlab/database/partitioning/detached_partition_dropper.rb b/lib/gitlab/database/partitioning/detached_partition_dropper.rb index dc63d93fd07..3e7ddece20b 100644 --- a/lib/gitlab/database/partitioning/detached_partition_dropper.rb +++ b/lib/gitlab/database/partitioning/detached_partition_dropper.rb @@ -7,18 +7,15 @@ module Gitlab return unless Feature.enabled?(:drop_detached_partitions, default_enabled: :yaml) Gitlab::AppLogger.info(message: "Checking for previously detached partitions to drop") + Postgresql::DetachedPartition.ready_to_drop.find_each do |detached_partition| - conn.transaction do + connection.transaction do # Another process may have already dropped the table and deleted this entry next unless (detached_partition = Postgresql::DetachedPartition.lock.find_by(id: detached_partition.id)) - unless check_partition_detached?(detached_partition) - Gitlab::AppLogger.error(message: "Attempt to drop attached database partition", partition_name: detached_partition.table_name) - detached_partition.destroy! - next - end + drop_detached_partition(detached_partition.table_name) - drop_one(detached_partition) + detached_partition.destroy! end rescue StandardError => e Gitlab::AppLogger.error(message: "Failed to drop previously detached partition", @@ -30,25 +27,30 @@ module Gitlab private - def drop_one(detached_partition) - conn.transaction do - conn.execute(<<~SQL) - DROP TABLE #{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.#{conn.quote_table_name(detached_partition.table_name)} - SQL + def drop_detached_partition(partition_name) + partition_identifier = qualify_partition_name(partition_name) + + if partition_detached?(partition_identifier) + connection.drop_table(partition_identifier, if_exists: true) - detached_partition.destroy! + Gitlab::AppLogger.info(message: "Dropped previously detached partition", partition_name: partition_name) + else + Gitlab::AppLogger.error(message: "Attempt to drop attached database partition", partition_name: partition_name) end - Gitlab::AppLogger.info(message: "Dropped previously detached partition", partition_name: detached_partition.table_name) end - def check_partition_detached?(detached_partition) + def qualify_partition_name(table_name) + "#{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.#{table_name}" + end + + def partition_detached?(partition_identifier) # PostgresPartition checks the pg_inherits view, so our partition will only show here if it's still attached # and thus should not be dropped - !PostgresPartition.for_identifier("#{Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA}.#{detached_partition.table_name}").exists? + !Gitlab::Database::PostgresPartition.for_identifier(partition_identifier).exists? end - def conn - @conn ||= ApplicationRecord.connection + def connection + Postgresql::DetachedPartition.connection end end end diff --git a/lib/gitlab/database/partitioning/multi_database_partition_dropper.rb b/lib/gitlab/database/partitioning/multi_database_partition_dropper.rb new file mode 100644 index 00000000000..769b658bae4 --- /dev/null +++ b/lib/gitlab/database/partitioning/multi_database_partition_dropper.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Gitlab + module Database + module Partitioning + class MultiDatabasePartitionDropper + def drop_detached_partitions + Gitlab::AppLogger.info(message: "Dropping detached postgres partitions") + + each_database_connection do |name, connection| + Gitlab::Database::SharedModel.using_connection(connection) do + Gitlab::AppLogger.debug(message: "Switched database connection", connection_name: name) + + DetachedPartitionDropper.new.perform + end + end + + Gitlab::AppLogger.info(message: "Finished dropping detached postgres partitions") + end + + private + + def each_database_connection + databases.each_pair do |name, connection_wrapper| + yield name, connection_wrapper.scope.connection + end + end + + def databases + Gitlab::Database.databases + end + end + end + end +end diff --git a/lib/gitlab/database/shared_model.rb b/lib/gitlab/database/shared_model.rb index 8f256758961..f304c32d731 100644 --- a/lib/gitlab/database/shared_model.rb +++ b/lib/gitlab/database/shared_model.rb @@ -2,6 +2,7 @@ module Gitlab module Database + # This abstract class is used for models which need to exist in multiple de-composed databases. class SharedModel < ActiveRecord::Base self.abstract_class = true diff --git a/lib/gitlab/usage_data_counters/guest_package_events.yml b/lib/gitlab/usage_data_counters/guest_package_events.yml deleted file mode 100644 index a9b9f8ea235..00000000000 --- a/lib/gitlab/usage_data_counters/guest_package_events.yml +++ /dev/null @@ -1,34 +0,0 @@ ---- -- i_package_composer_guest_delete -- i_package_composer_guest_pull -- i_package_composer_guest_push -- i_package_conan_guest_delete -- i_package_conan_guest_pull -- i_package_conan_guest_push -- i_package_container_guest_delete -- i_package_container_guest_pull -- i_package_container_guest_push -- i_package_debian_guest_delete -- i_package_debian_guest_pull -- i_package_debian_guest_push -- i_package_generic_guest_delete -- i_package_generic_guest_pull -- i_package_generic_guest_push -- i_package_golang_guest_delete -- i_package_golang_guest_pull -- i_package_golang_guest_push -- i_package_maven_guest_delete -- i_package_maven_guest_pull -- i_package_maven_guest_push -- i_package_npm_guest_delete -- i_package_npm_guest_pull -- i_package_npm_guest_push -- i_package_nuget_guest_delete -- i_package_nuget_guest_pull -- i_package_nuget_guest_push -- i_package_pypi_guest_delete -- i_package_pypi_guest_pull -- i_package_pypi_guest_push -- i_package_tag_guest_delete -- i_package_tag_guest_pull -- i_package_tag_guest_push diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 4ff561ad1f0..b32f8c5f566 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -22373,6 +22373,15 @@ msgstr "" msgid "NamespaceStorageSize|push to your repository, create pipelines, create issues or add comments. To reduce storage capacity, delete unused repositories, artifacts, wikis, issues, and pipelines. To learn more about reducing storage capacity please visit our docs." msgstr "" +msgid "NamespaceUserCap|Pending users must be reviewed and approved by a group owner. Learn more about %{user_caps_link_start}User Caps%{link_end} and %{users_pending_approval_link_start}Users Pending Approval%{link_end}." +msgstr "" + +msgid "NamespaceUserCap|View pending user approvals" +msgstr "" + +msgid "NamespaceUserCap|Your group has reached its billable member limit" +msgstr "" + msgid "Namespaces" msgstr "" @@ -22490,9 +22499,6 @@ msgstr "" msgid "NetworkPolicies|IP/subnet" msgstr "" -msgid "NetworkPolicies|If you are using Auto DevOps, your %{monospacedStart}auto-deploy-values.yaml%{monospacedEnd} file will not be updated if you change a policy in this section. Auto DevOps users should make changes by following the %{linkStart}Container Network Policy documentation%{linkEnd}." -msgstr "" - msgid "NetworkPolicies|Invalid or empty policy" msgstr "" @@ -22517,18 +22523,12 @@ msgstr "" msgid "NetworkPolicies|New policy" msgstr "" -msgid "NetworkPolicies|No policies detected" -msgstr "" - msgid "NetworkPolicies|None selected" msgstr "" msgid "NetworkPolicies|Please %{installLinkStart}install%{installLinkEnd} and %{configureLinkStart}configure a Kubernetes Agent for this project%{configureLinkEnd} to enable alerts." msgstr "" -msgid "NetworkPolicies|Policies are a specification of how groups of pods are allowed to communicate with each other's network endpoints." -msgstr "" - msgid "NetworkPolicies|Policy %{policyName} was successfully changed" msgstr "" @@ -35069,9 +35069,6 @@ msgstr "" msgid "ThreatMonitoring|Packet Activity" msgstr "" -msgid "ThreatMonitoring|Policies" -msgstr "" - msgid "ThreatMonitoring|Requests" msgstr "" @@ -40528,6 +40525,9 @@ msgstr "" msgid "mrWidget|Failed to load deployment statistics" msgstr "" +msgid "mrWidget|Hide %{widget} details" +msgstr "" + msgid "mrWidget|If the %{missingBranchName} branch exists in your local repository, you can merge this merge request manually using the command line" msgstr "" @@ -40650,6 +40650,9 @@ msgstr "" msgid "mrWidget|Set by %{merge_author} to start a merge train when the pipeline succeeds" msgstr "" +msgid "mrWidget|Show %{widget} details" +msgstr "" + msgid "mrWidget|The changes were merged into" msgstr "" diff --git a/qa/qa/page/merge_request/show.rb b/qa/qa/page/merge_request/show.rb index 22bcad5994a..28f5d7b6998 100644 --- a/qa/qa/page/merge_request/show.rb +++ b/qa/qa/page/merge_request/show.rb @@ -201,7 +201,7 @@ module QA def has_pipeline_status?(text) # Pipelines can be slow, so we wait a bit longer than the usual 10 seconds - wait_until(sleep_interval: 5, reload: false) do + wait_until(max_duration: 120, sleep_interval: 5, reload: true) do has_element?(:merge_request_pipeline_info_content, text: text, wait: 15 ) end end diff --git a/spec/finders/projects/members/effective_access_level_finder_spec.rb b/spec/finders/projects/members/effective_access_level_finder_spec.rb index 1112dbd0d6e..33fbb5aca30 100644 --- a/spec/finders/projects/members/effective_access_level_finder_spec.rb +++ b/spec/finders/projects/members/effective_access_level_finder_spec.rb @@ -194,6 +194,7 @@ RSpec.describe Projects::Members::EffectiveAccessLevelFinder, '#execute' do context 'for a project that is shared with other group(s)' do let_it_be(:shared_with_group) { create(:group) } let_it_be(:user_from_shared_with_group) { create(:user) } + let_it_be(:project) { create(:project, group: create(:group)) } before do create(:project_group_link, :developer, project: project, group: shared_with_group) @@ -211,9 +212,24 @@ RSpec.describe Projects::Members::EffectiveAccessLevelFinder, '#execute' do ) end - context 'when the group containing the project has forbidden group shares for any of its projects' do - let_it_be(:project) { create(:project, group: create(:group)) } + context 'even when the `lock_memberships_to_ldap` setting has been turned ON' do + before do + stub_application_setting(lock_memberships_to_ldap: true) + end + it 'includes the least among the specified access levels' do + expect(subject).to( + include( + hash_including( + 'user_id' => user_from_shared_with_group.id, + 'access_level' => Gitlab::Access::DEVELOPER + ) + ) + ) + end + end + + context 'when the group containing the project has forbidden group shares for any of its projects' do before do project.namespace.update!(share_with_group_lock: true) end diff --git a/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb b/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb index 8523b7104f0..8c406c90e36 100644 --- a/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb +++ b/spec/lib/gitlab/database/partitioning/detached_partition_dropper_spec.rb @@ -84,6 +84,7 @@ RSpec.describe Gitlab::Database::Partitioning::DetachedPartitionDropper do before do stub_feature_flags(drop_detached_partitions: false) end + it 'does not drop the partition' do subject.perform @@ -162,8 +163,8 @@ RSpec.describe Gitlab::Database::Partitioning::DetachedPartitionDropper do context 'when the first drop returns an error' do it 'still drops the second partition' do - expect(subject).to receive(:drop_one).ordered.and_raise('injected error') - expect(subject).to receive(:drop_one).ordered.and_call_original + expect(subject).to receive(:drop_detached_partition).ordered.and_raise('injected error') + expect(subject).to receive(:drop_detached_partition).ordered.and_call_original subject.perform diff --git a/spec/lib/gitlab/database/partitioning/multi_database_partition_dropper_spec.rb b/spec/lib/gitlab/database/partitioning/multi_database_partition_dropper_spec.rb new file mode 100644 index 00000000000..56d6ebb7aff --- /dev/null +++ b/spec/lib/gitlab/database/partitioning/multi_database_partition_dropper_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::Database::Partitioning::MultiDatabasePartitionDropper, '#drop_detached_partitions' do + subject(:drop_detached_partitions) { multi_db_dropper.drop_detached_partitions } + + let(:multi_db_dropper) { described_class.new } + + let(:connection_wrapper1) { double(scope: scope1) } + let(:connection_wrapper2) { double(scope: scope2) } + + let(:scope1) { double(connection: connection1) } + let(:scope2) { double(connection: connection2) } + + let(:connection1) { double('connection') } + let(:connection2) { double('connection') } + + let(:dropper_class) { Gitlab::Database::Partitioning::DetachedPartitionDropper } + let(:dropper1) { double('partition dropper') } + let(:dropper2) { double('partition dropper') } + + before do + allow(multi_db_dropper).to receive(:databases).and_return({ db1: connection_wrapper1, db2: connection_wrapper2 }) + end + + it 'drops detached partitions for each database' do + expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(connection1).and_yield.ordered + expect(dropper_class).to receive(:new).and_return(dropper1).ordered + expect(dropper1).to receive(:perform) + + expect(Gitlab::Database::SharedModel).to receive(:using_connection).with(connection2).and_yield.ordered + expect(dropper_class).to receive(:new).and_return(dropper2).ordered + expect(dropper2).to receive(:perform) + + drop_detached_partitions + end +end diff --git a/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb b/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb index 8f1f5b5ba1b..7c4cfcfb3a9 100644 --- a/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb +++ b/spec/lib/gitlab/database/partitioning/partition_manager_spec.rb @@ -176,7 +176,7 @@ RSpec.describe Gitlab::Database::Partitioning::PartitionManager do end it 'detaches exactly one partition' do - expect { subject }.to change { find_partitions(my_model.table_name, schema: Gitlab::Database::DYNAMIC_PARTITIONS_SCHEMA).size }.from(9).to(8) + expect { subject }.to change { find_partitions(my_model.table_name).size }.from(9).to(8) end it 'detaches the old partition' do diff --git a/spec/lib/gitlab/database/partitioning_spec.rb b/spec/lib/gitlab/database/partitioning_spec.rb index f163b45e01e..e539a8d0917 100644 --- a/spec/lib/gitlab/database/partitioning_spec.rb +++ b/spec/lib/gitlab/database/partitioning_spec.rb @@ -33,4 +33,16 @@ RSpec.describe Gitlab::Database::Partitioning do end end end + + describe '.drop_detached_partitions' do + let(:partition_dropper_class) { described_class::MultiDatabasePartitionDropper } + + it 'delegates to the partition dropper' do + expect_next_instance_of(partition_dropper_class) do |partition_dropper| + expect(partition_dropper).to receive(:drop_detached_partitions) + end + + described_class.drop_detached_partitions + end + end end diff --git a/spec/lib/gitlab/project_authorizations_spec.rb b/spec/lib/gitlab/project_authorizations_spec.rb index d2b41ee31d9..16066934194 100644 --- a/spec/lib/gitlab/project_authorizations_spec.rb +++ b/spec/lib/gitlab/project_authorizations_spec.rb @@ -204,6 +204,43 @@ RSpec.describe Gitlab::ProjectAuthorizations do end end + context 'with shared projects' do + let_it_be(:shared_with_group) { create(:group) } + let_it_be(:user) { create(:user) } + let_it_be(:project) { create(:project, group: create(:group)) } + + let(:mapping) { map_access_levels(authorizations) } + + before do + create(:project_group_link, :developer, project: project, group: shared_with_group) + shared_with_group.add_maintainer(user) + end + + it 'creates proper authorizations' do + expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER) + end + + context 'even when the `lock_memberships_to_ldap` setting has been turned ON' do + before do + stub_application_setting(lock_memberships_to_ldap: true) + end + + it 'creates proper authorizations' do + expect(mapping[project.id]).to eq(Gitlab::Access::DEVELOPER) + end + end + + context 'when the group containing the project has forbidden group shares for any of its projects' do + before do + project.namespace.update!(share_with_group_lock: true) + end + + it 'does not create authorizations' do + expect(mapping[project.id]).to be_nil + end + end + end + context 'with shared groups' do let(:parent_group_user) { create(:user) } let(:group_user) { create(:user) } diff --git a/spec/workers/database/drop_detached_partitions_worker_spec.rb b/spec/workers/database/drop_detached_partitions_worker_spec.rb index 42c3fa3c188..8693878ddd5 100644 --- a/spec/workers/database/drop_detached_partitions_worker_spec.rb +++ b/spec/workers/database/drop_detached_partitions_worker_spec.rb @@ -6,16 +6,15 @@ RSpec.describe Database::DropDetachedPartitionsWorker do describe '#perform' do subject { described_class.new.perform } - let(:dropper) { instance_double('DropDetachedPartitions', perform: nil) } let(:monitoring) { instance_double('PartitionMonitoring', report_metrics: nil) } before do - allow(Gitlab::Database::Partitioning::DetachedPartitionDropper).to receive(:new).and_return(dropper) + allow(Gitlab::Database::Partitioning).to receive(:drop_detached_partitions) allow(Gitlab::Database::Partitioning::PartitionMonitoring).to receive(:new).and_return(monitoring) end - it 'delegates to DropPartitionsPendingDrop' do - expect(dropper).to receive(:perform) + it 'delegates to Partitioning.drop_detached_partitions' do + expect(Gitlab::Database::Partitioning).to receive(:drop_detached_partitions) subject end diff --git a/vendor/project_templates/cluster_management.tar.gz b/vendor/project_templates/cluster_management.tar.gz Binary files differindex 9b168724c79..015b943dba7 100644 --- a/vendor/project_templates/cluster_management.tar.gz +++ b/vendor/project_templates/cluster_management.tar.gz |