Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									cea25900b7
								
							
						
					
					
						commit
						06d4ce7ee0
					
				| 
						 | 
				
			
			@ -1,4 +1,3 @@
 | 
			
		|||
/* eslint-disable no-new */
 | 
			
		||||
import { debounce } from 'lodash';
 | 
			
		||||
import {
 | 
			
		||||
  init as initConfidentialMergeRequest,
 | 
			
		||||
| 
						 | 
				
			
			@ -8,7 +7,7 @@ import {
 | 
			
		|||
import confidentialMergeRequestState from './confidential_merge_request/state';
 | 
			
		||||
import DropLab from './droplab/drop_lab';
 | 
			
		||||
import ISetter from './droplab/plugins/input_setter';
 | 
			
		||||
import { deprecatedCreateFlash as Flash } from './flash';
 | 
			
		||||
import createFlash from './flash';
 | 
			
		||||
import axios from './lib/utils/axios_utils';
 | 
			
		||||
import { __, sprintf } from './locale';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +131,9 @@ export default class CreateMergeRequestDropdown {
 | 
			
		|||
      .catch(() => {
 | 
			
		||||
        this.unavailable();
 | 
			
		||||
        this.disable();
 | 
			
		||||
        Flash(__('Failed to check related branches.'));
 | 
			
		||||
        createFlash({
 | 
			
		||||
          message: __('Failed to check related branches.'),
 | 
			
		||||
        });
 | 
			
		||||
      });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -147,7 +148,11 @@ export default class CreateMergeRequestDropdown {
 | 
			
		|||
        this.branchCreated = true;
 | 
			
		||||
        window.location.href = data.url;
 | 
			
		||||
      })
 | 
			
		||||
      .catch(() => Flash(__('Failed to create a branch for this issue. Please try again.')));
 | 
			
		||||
      .catch(() =>
 | 
			
		||||
        createFlash({
 | 
			
		||||
          message: __('Failed to create a branch for this issue. Please try again.'),
 | 
			
		||||
        }),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  createMergeRequest() {
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +168,11 @@ export default class CreateMergeRequestDropdown {
 | 
			
		|||
        this.mergeRequestCreated = true;
 | 
			
		||||
        window.location.href = data.url;
 | 
			
		||||
      })
 | 
			
		||||
      .catch(() => Flash(__('Failed to create Merge Request. Please try again.')));
 | 
			
		||||
      .catch(() =>
 | 
			
		||||
        createFlash({
 | 
			
		||||
          message: __('Failed to create Merge Request. Please try again.'),
 | 
			
		||||
        }),
 | 
			
		||||
      );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  disable() {
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +265,9 @@ export default class CreateMergeRequestDropdown {
 | 
			
		|||
      .catch(() => {
 | 
			
		||||
        this.unavailable();
 | 
			
		||||
        this.disable();
 | 
			
		||||
        new Flash(__('Failed to get ref.'));
 | 
			
		||||
        createFlash({
 | 
			
		||||
          message: __('Failed to get ref.'),
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.isGettingRef = false;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import { __ } from '~/locale';
 | 
			
		||||
import Ajax from '../droplab/plugins/ajax';
 | 
			
		||||
import Filter from '../droplab/plugins/filter';
 | 
			
		||||
import { deprecatedCreateFlash as Flash } from '../flash';
 | 
			
		||||
import createFlash from '../flash';
 | 
			
		||||
import DropdownUtils from './dropdown_utils';
 | 
			
		||||
import FilteredSearchDropdown from './filtered_search_dropdown';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -14,9 +14,9 @@ export default class DropdownEmoji extends FilteredSearchDropdown {
 | 
			
		|||
        method: 'setData',
 | 
			
		||||
        loadingTemplate: this.loadingTemplate,
 | 
			
		||||
        onError() {
 | 
			
		||||
          /* eslint-disable no-new */
 | 
			
		||||
          new Flash(__('An error occurred fetching the dropdown data.'));
 | 
			
		||||
          /* eslint-enable no-new */
 | 
			
		||||
          createFlash({
 | 
			
		||||
            message: __('An error occurred fetching the dropdown data.'),
 | 
			
		||||
          });
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      Filter: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
import { __ } from '~/locale';
 | 
			
		||||
import Ajax from '../droplab/plugins/ajax';
 | 
			
		||||
import Filter from '../droplab/plugins/filter';
 | 
			
		||||
import { deprecatedCreateFlash as Flash } from '../flash';
 | 
			
		||||
import createFlash from '../flash';
 | 
			
		||||
import DropdownUtils from './dropdown_utils';
 | 
			
		||||
import FilteredSearchDropdown from './filtered_search_dropdown';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -17,9 +17,9 @@ export default class DropdownNonUser extends FilteredSearchDropdown {
 | 
			
		|||
        loadingTemplate: this.loadingTemplate,
 | 
			
		||||
        preprocessing,
 | 
			
		||||
        onError() {
 | 
			
		||||
          /* eslint-disable no-new */
 | 
			
		||||
          new Flash(__('An error occurred fetching the dropdown data.'));
 | 
			
		||||
          /* eslint-enable no-new */
 | 
			
		||||
          createFlash({
 | 
			
		||||
            message: __('An error occurred fetching the dropdown data.'),
 | 
			
		||||
          });
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
      Filter: {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ import {
 | 
			
		|||
  DOWN_KEY_CODE,
 | 
			
		||||
} from '~/lib/utils/keycodes';
 | 
			
		||||
import { __ } from '~/locale';
 | 
			
		||||
import { deprecatedCreateFlash as Flash } from '../flash';
 | 
			
		||||
import createFlash from '../flash';
 | 
			
		||||
import { addClassIfElementExists } from '../lib/utils/dom_utils';
 | 
			
		||||
import { visitUrl } from '../lib/utils/url_utility';
 | 
			
		||||
import FilteredSearchContainer from './container';
 | 
			
		||||
| 
						 | 
				
			
			@ -92,8 +92,9 @@ export default class FilteredSearchManager {
 | 
			
		|||
      .fetch()
 | 
			
		||||
      .catch((error) => {
 | 
			
		||||
        if (error.name === 'RecentSearchesServiceError') return undefined;
 | 
			
		||||
        // eslint-disable-next-line no-new
 | 
			
		||||
        new Flash(__('An error occurred while parsing recent searches'));
 | 
			
		||||
        createFlash({
 | 
			
		||||
          message: __('An error occurred while parsing recent searches'),
 | 
			
		||||
        });
 | 
			
		||||
        // Gracefully fail to empty array
 | 
			
		||||
        return [];
 | 
			
		||||
      })
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,7 +22,7 @@ import syntaxHighlight from '~/syntax_highlight';
 | 
			
		|||
import Autosave from './autosave';
 | 
			
		||||
import loadAwardsHandler from './awards_handler';
 | 
			
		||||
import CommentTypeToggle from './comment_type_toggle';
 | 
			
		||||
import { deprecatedCreateFlash as Flash } from './flash';
 | 
			
		||||
import createFlash from './flash';
 | 
			
		||||
import { defaultAutocompleteConfig } from './gfm_auto_complete';
 | 
			
		||||
import GLForm from './gl_form';
 | 
			
		||||
import axios from './lib/utils/axios_utils';
 | 
			
		||||
| 
						 | 
				
			
			@ -399,7 +399,11 @@ export default class Notes {
 | 
			
		|||
        if (noteEntity.commands_changes && Object.keys(noteEntity.commands_changes).length > 0) {
 | 
			
		||||
          $notesList.find('.system-note.being-posted').remove();
 | 
			
		||||
        }
 | 
			
		||||
        this.addFlash(noteEntity.errors.commands_only, 'notice', this.parentTimeline.get(0));
 | 
			
		||||
        this.addFlash({
 | 
			
		||||
          message: noteEntity.errors.commands_only,
 | 
			
		||||
          type: 'notice',
 | 
			
		||||
          parent: this.parentTimeline.get(0),
 | 
			
		||||
        });
 | 
			
		||||
        this.refresh();
 | 
			
		||||
      }
 | 
			
		||||
      return;
 | 
			
		||||
| 
						 | 
				
			
			@ -620,20 +624,21 @@ export default class Notes {
 | 
			
		|||
    } else if ($form.hasClass('js-discussion-note-form')) {
 | 
			
		||||
      formParentTimeline = $form.closest('.discussion-notes').find('.notes');
 | 
			
		||||
    }
 | 
			
		||||
    return this.addFlash(
 | 
			
		||||
      __(
 | 
			
		||||
    return this.addFlash({
 | 
			
		||||
      message: __(
 | 
			
		||||
        'Your comment could not be submitted! Please check your network connection and try again.',
 | 
			
		||||
      ),
 | 
			
		||||
      'alert',
 | 
			
		||||
      formParentTimeline.get(0),
 | 
			
		||||
    );
 | 
			
		||||
      type: 'alert',
 | 
			
		||||
      parent: formParentTimeline.get(0),
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  updateNoteError() {
 | 
			
		||||
    // eslint-disable-next-line no-new
 | 
			
		||||
    new Flash(
 | 
			
		||||
      __('Your comment could not be updated! Please check your network connection and try again.'),
 | 
			
		||||
    );
 | 
			
		||||
    createFlash({
 | 
			
		||||
      message: __(
 | 
			
		||||
        'Your comment could not be updated! Please check your network connection and try again.',
 | 
			
		||||
      ),
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
| 
						 | 
				
			
			@ -1289,7 +1294,7 @@ export default class Notes {
 | 
			
		|||
  }
 | 
			
		||||
 | 
			
		||||
  addFlash(...flashParams) {
 | 
			
		||||
    this.flashContainer = new Flash(...flashParams);
 | 
			
		||||
    this.flashContainer = createFlash(...flashParams);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  clearFlash() {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ export const EXCLUDED_NODES = ['OPTION'];
 | 
			
		|||
export const HIDE_CLASS = 'gl-display-none';
 | 
			
		||||
 | 
			
		||||
// used to highlight the text that matches the * search term
 | 
			
		||||
export const HIGHLIGHT_CLASS = 'gl-bg-orange-50';
 | 
			
		||||
export const HIGHLIGHT_CLASS = 'gl-bg-orange-100';
 | 
			
		||||
 | 
			
		||||
// How many seconds to wait until the user * stops typing
 | 
			
		||||
export const TYPING_DELAY = 400;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,10 +15,10 @@ import { isEmpty } from 'lodash';
 | 
			
		|||
import readyToMergeMixin from 'ee_else_ce/vue_merge_request_widget/mixins/ready_to_merge';
 | 
			
		||||
import readyToMergeQuery from 'ee_else_ce/vue_merge_request_widget/queries/states/ready_to_merge.query.graphql';
 | 
			
		||||
import { refreshUserMergeRequestCounts } from '~/commons/nav/user_merge_requests';
 | 
			
		||||
import createFlash from '~/flash';
 | 
			
		||||
import simplePoll from '~/lib/utils/simple_poll';
 | 
			
		||||
import { __ } from '~/locale';
 | 
			
		||||
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
 | 
			
		||||
import { deprecatedCreateFlash as Flash } from '../../../flash';
 | 
			
		||||
import MergeRequest from '../../../merge_request';
 | 
			
		||||
import { AUTO_MERGE_STRATEGIES, DANGER, INFO, WARNING } from '../../constants';
 | 
			
		||||
import eventHub from '../../event_hub';
 | 
			
		||||
| 
						 | 
				
			
			@ -351,7 +351,9 @@ export default {
 | 
			
		|||
        })
 | 
			
		||||
        .catch(() => {
 | 
			
		||||
          this.isMakingRequest = false;
 | 
			
		||||
          new Flash(__('Something went wrong. Please try again.')); // eslint-disable-line
 | 
			
		||||
          createFlash({
 | 
			
		||||
            message: __('Something went wrong. Please try again.'),
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
    handleMergeImmediatelyButtonClick() {
 | 
			
		||||
| 
						 | 
				
			
			@ -402,7 +404,9 @@ export default {
 | 
			
		|||
          }
 | 
			
		||||
        })
 | 
			
		||||
        .catch(() => {
 | 
			
		||||
          new Flash(__('Something went wrong while merging this merge request. Please try again.')); // eslint-disable-line
 | 
			
		||||
          createFlash({
 | 
			
		||||
            message: __('Something went wrong while merging this merge request. Please try again.'),
 | 
			
		||||
          });
 | 
			
		||||
          stopPolling();
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
| 
						 | 
				
			
			@ -432,7 +436,9 @@ export default {
 | 
			
		|||
          }
 | 
			
		||||
        })
 | 
			
		||||
        .catch(() => {
 | 
			
		||||
          new Flash(__('Something went wrong while deleting the source branch. Please try again.')); // eslint-disable-line
 | 
			
		||||
          createFlash({
 | 
			
		||||
            message: __('Something went wrong while deleting the source branch. Please try again.'),
 | 
			
		||||
          });
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
 | 
			
		|||
    push_frontend_feature_flag(:local_file_reviews, default_enabled: :yaml)
 | 
			
		||||
    push_frontend_feature_flag(:paginated_notes, @project, default_enabled: :yaml)
 | 
			
		||||
    push_frontend_feature_flag(:new_pipelines_table, @project, default_enabled: :yaml)
 | 
			
		||||
    push_frontend_feature_flag(:confidential_notes, @project, default_enabled: :yaml)
 | 
			
		||||
 | 
			
		||||
    record_experiment_user(:invite_members_version_b)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,20 +2,29 @@
 | 
			
		|||
 | 
			
		||||
class RemoveExpiredMembersWorker # rubocop:disable Scalability/IdempotentWorker
 | 
			
		||||
  include ApplicationWorker
 | 
			
		||||
  include CronjobQueue # rubocop:disable Scalability/CronWorkerContext
 | 
			
		||||
  include CronjobQueue
 | 
			
		||||
 | 
			
		||||
  feature_category :authentication_and_authorization
 | 
			
		||||
  worker_resource_boundary :cpu
 | 
			
		||||
 | 
			
		||||
  # rubocop: disable CodeReuse/ActiveRecord
 | 
			
		||||
  def perform
 | 
			
		||||
    Member.expired.preload(:user).find_each do |member|
 | 
			
		||||
      Members::DestroyService.new.execute(member, skip_authorization: true)
 | 
			
		||||
    Member.expired.preload(:user, :source).find_each do |member|
 | 
			
		||||
      context = {
 | 
			
		||||
        user: member.user,
 | 
			
		||||
        # The ApplicationContext will reject type-mismatches. So a GroupMemeber will only populate `namespace`.
 | 
			
		||||
        # while a `ProjectMember` will populate `project
 | 
			
		||||
        project: member.source,
 | 
			
		||||
        namespace: member.source
 | 
			
		||||
      }
 | 
			
		||||
      with_context(context) do
 | 
			
		||||
        Members::DestroyService.new.execute(member, skip_authorization: true)
 | 
			
		||||
 | 
			
		||||
      expired_user = member.user
 | 
			
		||||
        expired_user = member.user
 | 
			
		||||
 | 
			
		||||
      if expired_user.project_bot?
 | 
			
		||||
        Users::DestroyService.new(nil).execute(expired_user, skip_authorization: true)
 | 
			
		||||
        if expired_user.project_bot?
 | 
			
		||||
          Users::DestroyService.new(nil).execute(expired_user, skip_authorization: true)
 | 
			
		||||
        end
 | 
			
		||||
      end
 | 
			
		||||
    rescue => ex
 | 
			
		||||
      logger.error("Expired Member ID=#{member.id} cannot be removed - #{ex}")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
---
 | 
			
		||||
title: Fix ruby alpine CI template
 | 
			
		||||
merge_request: 57109
 | 
			
		||||
author:
 | 
			
		||||
type: fixed
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
---
 | 
			
		||||
title: Update weight transaltion for Russian locale
 | 
			
		||||
merge_request: 56986
 | 
			
		||||
author: Gennady Kovalev (@belolap)
 | 
			
		||||
type: fixed
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
---
 | 
			
		||||
title: Push confidential_notes feature flag to mr frontend
 | 
			
		||||
merge_request: 56798
 | 
			
		||||
author: Lee Tickett @leetickett
 | 
			
		||||
type: fixed
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
---
 | 
			
		||||
title: Enable DISTINCT optimization for ObjectHierarchy globally
 | 
			
		||||
merge_request: 57052
 | 
			
		||||
author:
 | 
			
		||||
type: changed
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,8 @@
 | 
			
		|||
---
 | 
			
		||||
name: use_distinct_for_all_object_hierarchy
 | 
			
		||||
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/57052
 | 
			
		||||
rollout_issue_url: 
 | 
			
		||||
milestone: '13.11'
 | 
			
		||||
type: development
 | 
			
		||||
group: group::database
 | 
			
		||||
default_enabled: false
 | 
			
		||||
| 
						 | 
				
			
			@ -274,6 +274,9 @@ with secure tokens as you complete the setup process.
 | 
			
		|||
 | 
			
		||||
We note in the instructions below where these secrets are required.
 | 
			
		||||
 | 
			
		||||
NOTE:
 | 
			
		||||
Omnibus GitLab installations can use `gitlab-secrets.json`.
 | 
			
		||||
 | 
			
		||||
### PostgreSQL
 | 
			
		||||
 | 
			
		||||
NOTE:
 | 
			
		||||
| 
						 | 
				
			
			@ -283,10 +286,11 @@ database on the same PostgreSQL server if using
 | 
			
		|||
of GitLab and should not be replicated.
 | 
			
		||||
 | 
			
		||||
These instructions help set up a single PostgreSQL database, which creates a single point of
 | 
			
		||||
failure. For greater fault tolerance, the following options are available:
 | 
			
		||||
failure. The following options are available:
 | 
			
		||||
 | 
			
		||||
- For non-Geo installations, use one of the fault-tolerant
 | 
			
		||||
  [PostgreSQL setups](../postgresql/index.md).
 | 
			
		||||
- For non-Geo installations, either:
 | 
			
		||||
  - Use one of the documented [PostgreSQL setups](../postgresql/index.md).
 | 
			
		||||
  - Use your own third-party database setup, if fault tolerance is required.
 | 
			
		||||
- For Geo instances, either:
 | 
			
		||||
  - Set up a separate [PostgreSQL instance](https://www.postgresql.org/docs/11/high-availability.html).
 | 
			
		||||
  - Use a cloud-managed PostgreSQL service. AWS
 | 
			
		||||
| 
						 | 
				
			
			@ -802,14 +806,26 @@ documentation](configure_gitaly.md#configure-gitaly-servers).
 | 
			
		|||
   gitaly['auth_token'] = 'PRAEFECT_INTERNAL_TOKEN'
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
1. Configure the GitLab Shell `secret_token`, and `internal_api_url` which are
 | 
			
		||||
   needed for `git push` operations.
 | 
			
		||||
1. Configure the GitLab Shell secret token, which is needed for `git push` operations. Either:
 | 
			
		||||
 | 
			
		||||
   If you have already configured [Gitaly on its own server](index.md)
 | 
			
		||||
   - Method 1:
 | 
			
		||||
 | 
			
		||||
     1. Copy `/etc/gitlab/gitlab-secrets.json` from the Gitaly client to same path on the Gitaly
 | 
			
		||||
        servers and any other Gitaly clients.
 | 
			
		||||
     1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) on Gitaly servers.
 | 
			
		||||
 | 
			
		||||
   - Method 2:
 | 
			
		||||
 | 
			
		||||
     1. Edit `/etc/gitlab/gitlab.rb`.
 | 
			
		||||
     1. Replace `GITLAB_SHELL_SECRET_TOKEN` with the real secret.
 | 
			
		||||
 | 
			
		||||
        ```ruby
 | 
			
		||||
        gitlab_shell['secret_token'] = 'GITLAB_SHELL_SECRET_TOKEN'
 | 
			
		||||
        ```
 | 
			
		||||
 | 
			
		||||
1. Configure and `internal_api_url`, which is also needed for `git push` operations:
 | 
			
		||||
 | 
			
		||||
   ```ruby
 | 
			
		||||
   gitlab_shell['secret_token'] = 'GITLAB_SHELL_SECRET_TOKEN'
 | 
			
		||||
 | 
			
		||||
   # Configure the gitlab-shell API callback URL. Without this, `git push` will
 | 
			
		||||
   # fail. This can be your front door GitLab URL or an internal load balancer.
 | 
			
		||||
   # Examples: 'https://gitlab.example.com', 'http://1.2.3.4'
 | 
			
		||||
| 
						 | 
				
			
			@ -961,15 +977,23 @@ Particular attention should be shown to:
 | 
			
		|||
   })
 | 
			
		||||
   ```
 | 
			
		||||
 | 
			
		||||
1. Configure the `gitlab_shell['secret_token']` so that callbacks from Gitaly
 | 
			
		||||
   nodes during a `git push` are properly authenticated by editing
 | 
			
		||||
   `/etc/gitlab/gitlab.rb`:
 | 
			
		||||
1. Configure the GitLab Shell secret token so that callbacks from Gitaly nodes during a `git push`
 | 
			
		||||
   are properly authenticated. Either:
 | 
			
		||||
 | 
			
		||||
   You need to replace `GITLAB_SHELL_SECRET_TOKEN` with the real secret.
 | 
			
		||||
   - Method 1:
 | 
			
		||||
 | 
			
		||||
   ```ruby
 | 
			
		||||
   gitlab_shell['secret_token'] = 'GITLAB_SHELL_SECRET_TOKEN'
 | 
			
		||||
   ```
 | 
			
		||||
     1. Copy `/etc/gitlab/gitlab-secrets.json` from the Gitaly client to same path on the Gitaly
 | 
			
		||||
        servers and any other Gitaly clients.
 | 
			
		||||
     1. [Reconfigure GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) on Gitaly servers.
 | 
			
		||||
 | 
			
		||||
   - Method 2:
 | 
			
		||||
 | 
			
		||||
     1. Edit `/etc/gitlab/gitlab.rb`.
 | 
			
		||||
     1. Replace `GITLAB_SHELL_SECRET_TOKEN` with the real secret.
 | 
			
		||||
 | 
			
		||||
        ```ruby
 | 
			
		||||
        gitlab_shell['secret_token'] = 'GITLAB_SHELL_SECRET_TOKEN'
 | 
			
		||||
        ```
 | 
			
		||||
 | 
			
		||||
1. Add Prometheus monitoring settings by editing `/etc/gitlab/gitlab.rb`. If Prometheus
 | 
			
		||||
   is enabled on a different node, make edits on that node instead.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1069,8 +1069,8 @@ job:
 | 
			
		|||
    - when: on_success
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
- If the pipeline is for a merge request, the job is **not** be added to the pipeline.
 | 
			
		||||
- If the pipeline is a scheduled pipeline, the job is **not** be added to the pipeline.
 | 
			
		||||
- If the pipeline is for a merge request, the job is **not** added to the pipeline.
 | 
			
		||||
- If the pipeline is a scheduled pipeline, the job is **not** added to the pipeline.
 | 
			
		||||
- In **all other cases**, the job is added to the pipeline, with `when: on_success`.
 | 
			
		||||
 | 
			
		||||
WARNING:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 73 KiB  | 
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 25 KiB  | 
| 
						 | 
				
			
			@ -7,12 +7,10 @@ type: index, reference
 | 
			
		|||
 | 
			
		||||
# Merge requests **(FREE)**
 | 
			
		||||
 | 
			
		||||
Whenever you need to merge one branch into another branch with GitLab, you
 | 
			
		||||
must create a merge request (MR).
 | 
			
		||||
Merge requests (MRs) are the way you check source code changes into a branch.
 | 
			
		||||
 | 
			
		||||
Using merge requests, you can visualize and collaborate on proposed changes to
 | 
			
		||||
source code. Merge requests display information about the proposed code changes,
 | 
			
		||||
including:
 | 
			
		||||
When you open a merge request, you can visualize and collaborate on the code changes before merge.
 | 
			
		||||
Merge requests include:
 | 
			
		||||
 | 
			
		||||
- A description of the request.
 | 
			
		||||
- Code changes and inline code reviews.
 | 
			
		||||
| 
						 | 
				
			
			@ -20,55 +18,50 @@ including:
 | 
			
		|||
- A comment section for discussion threads.
 | 
			
		||||
- The list of commits.
 | 
			
		||||
 | 
			
		||||
Based on your workflow, after review you can merge a merge request into its
 | 
			
		||||
target branch.
 | 
			
		||||
 | 
			
		||||
To get started, read the [introduction to merge requests](getting_started.md).
 | 
			
		||||
 | 
			
		||||
## Use cases
 | 
			
		||||
## Merge request workflows
 | 
			
		||||
 | 
			
		||||
A. Consider you're a software developer working in a team:
 | 
			
		||||
For a software developer working in a team:
 | 
			
		||||
 | 
			
		||||
1. You checkout a new branch, and submit your changes through a merge request
 | 
			
		||||
1. You gather feedback from your team
 | 
			
		||||
1. You work on the implementation optimizing code with [Code Quality reports](code_quality.md)
 | 
			
		||||
1. You verify your changes with [Unit test reports](../../../ci/unit_test_reports.md) in GitLab CI/CD
 | 
			
		||||
1. You avoid using dependencies whose license is not compatible with your project with [License Compliance reports](../../compliance/license_compliance/index.md) **(ULTIMATE)**
 | 
			
		||||
1. You request the [approval](merge_request_approvals.md) from your manager **(STARTER)**
 | 
			
		||||
1. You checkout a new branch, and submit your changes through a merge request.
 | 
			
		||||
1. You gather feedback from your team.
 | 
			
		||||
1. You work on the implementation optimizing code with [Code Quality reports](code_quality.md).
 | 
			
		||||
1. You verify your changes with [Unit test reports](../../../ci/unit_test_reports.md) in GitLab CI/CD.
 | 
			
		||||
1. You avoid using dependencies whose license is not compatible with your project with [License Compliance reports](../../compliance/license_compliance/index.md).
 | 
			
		||||
1. You request the [approval](merge_request_approvals.md) from your manager.
 | 
			
		||||
1. Your manager:
 | 
			
		||||
   1. Pushes a commit with their final review
 | 
			
		||||
   1. [Approves the merge request](merge_request_approvals.md) **(STARTER)**
 | 
			
		||||
   1. Sets it to [merge when pipeline succeeds](merge_when_pipeline_succeeds.md)
 | 
			
		||||
1. Your changes get deployed to production with [manual actions](../../../ci/yaml/README.md#whenmanual) for GitLab CI/CD
 | 
			
		||||
1. Your implementations were successfully shipped to your customer
 | 
			
		||||
   1. Pushes a commit with their final review.
 | 
			
		||||
   1. [Approves the merge request](merge_request_approvals.md).
 | 
			
		||||
   1. Sets it to [merge when pipeline succeeds](merge_when_pipeline_succeeds.md).
 | 
			
		||||
1. Your changes get deployed to production with [manual actions](../../../ci/yaml/README.md#whenmanual) for GitLab CI/CD.
 | 
			
		||||
1. Your implementations were successfully shipped to your customer.
 | 
			
		||||
 | 
			
		||||
B. Consider you're a web developer writing a webpage for your company's website:
 | 
			
		||||
For a web developer writing a webpage for your company's website:
 | 
			
		||||
 | 
			
		||||
1. You checkout a new branch, and submit a new page through a merge request
 | 
			
		||||
1. You gather feedback from your reviewers
 | 
			
		||||
1. Your changes are previewed with [Review Apps](../../../ci/review_apps/index.md)
 | 
			
		||||
1. You request your web designers for their implementation
 | 
			
		||||
1. You request the [approval](merge_request_approvals.md) from your manager **(STARTER)**
 | 
			
		||||
1. Once approved, your merge request is [squashed and merged](squash_and_merge.md), and [deployed to staging with GitLab Pages](https://about.gitlab.com/blog/2021/02/05/ci-deployment-and-environments/)
 | 
			
		||||
1. Your production team [cherry picks](cherry_pick_changes.md) the merge commit into production
 | 
			
		||||
1. You checkout a new branch and submit a new page through a merge request.
 | 
			
		||||
1. You gather feedback from your reviewers.
 | 
			
		||||
1. You preview your changes with [Review Apps](../../../ci/review_apps/index.md).
 | 
			
		||||
1. You request your web designers for their implementation.
 | 
			
		||||
1. You request the [approval](merge_request_approvals.md) from your manager.
 | 
			
		||||
1. Once approved, your merge request is [squashed and merged](squash_and_merge.md), and [deployed to staging with GitLab Pages](https://about.gitlab.com/blog/2021/02/05/ci-deployment-and-environments/).
 | 
			
		||||
1. Your production team [cherry picks](cherry_pick_changes.md) the merge commit into production.
 | 
			
		||||
 | 
			
		||||
## Merge request navigation tabs at the top
 | 
			
		||||
 | 
			
		||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/33813) in GitLab 12.6. This positioning is experimental.
 | 
			
		||||
 | 
			
		||||
So far, the navigation tabs present in merge requests to display **Discussion**,
 | 
			
		||||
**Commits**, **Pipelines**, and **Changes** were located after the merge request
 | 
			
		||||
In GitLab 12.5 and earlier, navigation tabs in merge requests (**Discussion**,
 | 
			
		||||
**Commits**, **Pipelines**, and **Changes**) were located after the merge request
 | 
			
		||||
widget.
 | 
			
		||||
 | 
			
		||||
To facilitate this navigation without having to scroll up and down through the page
 | 
			
		||||
to find these tabs, based on user feedback, we're experimenting with a new positioning
 | 
			
		||||
of these tabs. They are now located at the top of the merge request, with a new
 | 
			
		||||
**Overview** tab, containing the description of the merge request followed by the
 | 
			
		||||
widget. Next to **Overview**, you can find **Pipelines**, **Commits**, and **Changes**.
 | 
			
		||||
To facilitate navigation without scrolling, and based on user feedback, the tabs are
 | 
			
		||||
now located at the top of the merge request tab. A new **Overview** tab was added,
 | 
			
		||||
and next to **Overview** are **Commits**, **Pipelines**, and **Changes**.
 | 
			
		||||
 | 
			
		||||

 | 
			
		||||

 | 
			
		||||
 | 
			
		||||
Please note this change is currently behind a feature flag which is enabled by default. For
 | 
			
		||||
This change is behind a feature flag that is enabled by default. For
 | 
			
		||||
self-managed instances, it can be disabled through the Rails console by a GitLab
 | 
			
		||||
administrator with the following command:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -76,23 +69,9 @@ administrator with the following command:
 | 
			
		|||
Feature.disable(:mr_tabs_position)
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Creating merge requests
 | 
			
		||||
## Related topics
 | 
			
		||||
 | 
			
		||||
Learn [how to create a merge request](creating_merge_requests.md).
 | 
			
		||||
 | 
			
		||||
## Reviewing and managing merge requests
 | 
			
		||||
 | 
			
		||||
See the features at your disposal to [review and manage merge requests](reviewing_and_managing_merge_requests.md).
 | 
			
		||||
 | 
			
		||||
## Testing and reports in merge requests
 | 
			
		||||
 | 
			
		||||
Learn about the options for [testing and reports](testing_and_reports_in_merge_requests.md) on the changes in a merge request.
 | 
			
		||||
 | 
			
		||||
## Authorization for merge requests
 | 
			
		||||
 | 
			
		||||
There are two main ways to have a merge request flow with GitLab:
 | 
			
		||||
 | 
			
		||||
1. Working with [protected branches](../protected_branches.md) in a single repository
 | 
			
		||||
1. Working with forks of an authoritative project
 | 
			
		||||
 | 
			
		||||
[Learn more about the authorization for merge requests.](authorization_for_merge_requests.md)
 | 
			
		||||
- [Create a merge request](creating_merge_requests.md)
 | 
			
		||||
- [Review and manage merge requests](reviewing_and_managing_merge_requests.md)
 | 
			
		||||
- [Authorization for merge requests](authorization_for_merge_requests.md)
 | 
			
		||||
- [Testing and reports](testing_and_reports_in_merge_requests.md)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -344,7 +344,7 @@ terminal:
 | 
			
		|||
  # This can be any image that has the necessary runtime environment for your project.
 | 
			
		||||
  image: node:10-alpine
 | 
			
		||||
  before_script:
 | 
			
		||||
    - apt-get update
 | 
			
		||||
    - apk update
 | 
			
		||||
  script: sleep 60
 | 
			
		||||
  variables:
 | 
			
		||||
    RAILS_ENV: "test"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,7 +59,7 @@ module API
 | 
			
		|||
        project: -> { @project },
 | 
			
		||||
        namespace: -> { @group },
 | 
			
		||||
        runner: -> { @current_runner || @runner },
 | 
			
		||||
        caller_id: route.origin,
 | 
			
		||||
        caller_id: api_endpoint.endpoint_id,
 | 
			
		||||
        remote_ip: request.ip,
 | 
			
		||||
        feature_category: feature_category
 | 
			
		||||
      )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,6 +32,10 @@ module API
 | 
			
		|||
          end
 | 
			
		||||
        end.compact.to_set
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      def endpoint_id
 | 
			
		||||
        "#{request.request_method} #{route.origin}"
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ module API
 | 
			
		|||
        Gitlab::ApplicationContext.push(
 | 
			
		||||
          user: -> { actor&.user },
 | 
			
		||||
          project: -> { project },
 | 
			
		||||
          caller_id: route.origin,
 | 
			
		||||
          caller_id: api_endpoint.endpoint_id,
 | 
			
		||||
          remote_ip: request.ip,
 | 
			
		||||
          feature_category: feature_category
 | 
			
		||||
        )
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -68,12 +68,14 @@ module Gitlab
 | 
			
		|||
        expose_depth = hierarchy_order.present?
 | 
			
		||||
        hierarchy_order ||= :asc
 | 
			
		||||
 | 
			
		||||
        recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(model.all).distinct
 | 
			
		||||
 | 
			
		||||
        # if hierarchy_order is given, the calculated `depth` should be present in SELECT
 | 
			
		||||
        if expose_depth
 | 
			
		||||
          recursive_query = base_and_ancestors_cte(upto, hierarchy_order).apply_to(model.all).distinct
 | 
			
		||||
          read_only(model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table)).order(depth: hierarchy_order))
 | 
			
		||||
        else
 | 
			
		||||
          recursive_query = base_and_ancestors_cte(upto).apply_to(model.all)
 | 
			
		||||
          recursive_query = recursive_query.reselect(*recursive_query.arel.projections, 'ROW_NUMBER() OVER () as depth').distinct
 | 
			
		||||
          recursive_query = model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table))
 | 
			
		||||
          read_only(remove_depth_and_maintain_order(recursive_query, hierarchy_order: hierarchy_order))
 | 
			
		||||
        end
 | 
			
		||||
      else
 | 
			
		||||
| 
						 | 
				
			
			@ -93,11 +95,13 @@ module Gitlab
 | 
			
		|||
    def base_and_descendants(with_depth: false)
 | 
			
		||||
      if use_distinct?
 | 
			
		||||
        # Always calculate `depth`, remove it later if with_depth is false
 | 
			
		||||
        base_cte = base_and_descendants_cte(with_depth: true).apply_to(model.all).distinct
 | 
			
		||||
 | 
			
		||||
        if with_depth
 | 
			
		||||
          read_only(model.from(Arel::Nodes::As.new(recursive_query.arel, objects_table)).order(depth: :asc))
 | 
			
		||||
          base_cte = base_and_descendants_cte(with_depth: true).apply_to(model.all).distinct
 | 
			
		||||
          read_only(model.from(Arel::Nodes::As.new(base_cte.arel, objects_table)).order(depth: :asc))
 | 
			
		||||
        else
 | 
			
		||||
          base_cte = base_and_descendants_cte.apply_to(model.all)
 | 
			
		||||
          base_cte = base_cte.reselect(*base_cte.arel.projections, 'ROW_NUMBER() OVER () as depth').distinct
 | 
			
		||||
          base_cte = model.from(Arel::Nodes::As.new(base_cte.arel, objects_table))
 | 
			
		||||
          read_only(remove_depth_and_maintain_order(base_cte, hierarchy_order: :asc))
 | 
			
		||||
        end
 | 
			
		||||
      else
 | 
			
		||||
| 
						 | 
				
			
			@ -161,7 +165,12 @@ module Gitlab
 | 
			
		|||
 | 
			
		||||
    # Use distinct on the Namespace queries to avoid bad planner behavior in PG11.
 | 
			
		||||
    def use_distinct?
 | 
			
		||||
      (model <= Namespace) && options[:use_distinct]
 | 
			
		||||
      return unless model <= Namespace
 | 
			
		||||
      # Global use_distinct_for_all_object_hierarchy takes precedence over use_distinct_in_object_hierarchy
 | 
			
		||||
      return true if Feature.enabled?(:use_distinct_for_all_object_hierarchy)
 | 
			
		||||
      return options[:use_distinct] if options.key?(:use_distinct)
 | 
			
		||||
 | 
			
		||||
      false
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    # Remove the extra `depth` field using an INNER JOIN to avoid breaking UNION queries
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -523,7 +523,7 @@ msgid "%{completedCount} completed weight"
 | 
			
		|||
msgstr ""
 | 
			
		||||
 | 
			
		||||
msgid "%{completedWeight} of %{totalWeight} weight completed"
 | 
			
		||||
msgstr "Завершено %{completedWeight} из %{totalWeight} приоритета"
 | 
			
		||||
msgstr "Завершено %{completedWeight} из %{totalWeight} единиц веса"
 | 
			
		||||
 | 
			
		||||
msgid "%{containerScanningLinkStart}Container Scanning%{containerScanningLinkEnd} and/or %{dependencyScanningLinkStart}Dependency Scanning%{dependencyScanningLinkEnd} must be enabled. %{securityBotLinkStart}GitLab-Security-Bot%{securityBotLinkEnd} will be the author of the auto-created merge request. %{moreInfoLinkStart}More information%{moreInfoLinkEnd}."
 | 
			
		||||
msgstr ""
 | 
			
		||||
| 
						 | 
				
			
			@ -859,7 +859,7 @@ msgid "%{openedIssues} open, %{closedIssues} closed"
 | 
			
		|||
msgstr "%{openedIssues} открыто, %{closedIssues} закрыто"
 | 
			
		||||
 | 
			
		||||
msgid "%{percentage}%% weight completed"
 | 
			
		||||
msgstr "%{percentage}%% приоритета завершено"
 | 
			
		||||
msgstr "%{percentage}%% веса завершено"
 | 
			
		||||
 | 
			
		||||
msgid "%{percent}%% complete"
 | 
			
		||||
msgstr "%{percent}%% выполнено"
 | 
			
		||||
| 
						 | 
				
			
			@ -1068,13 +1068,13 @@ msgid "%{totalMemory} (%{freeSpacePercentage}%{percentSymbol} free)"
 | 
			
		|||
msgstr ""
 | 
			
		||||
 | 
			
		||||
msgid "%{totalWeight} total weight"
 | 
			
		||||
msgstr "общий приоритет %{totalWeight}"
 | 
			
		||||
msgstr "общий вес %{totalWeight}"
 | 
			
		||||
 | 
			
		||||
msgid "%{total_warnings} warning(s) found:"
 | 
			
		||||
msgstr ""
 | 
			
		||||
 | 
			
		||||
msgid "%{total} open issue weight"
 | 
			
		||||
msgstr "приоритет открытого обсуждения %{total}"
 | 
			
		||||
msgstr "вес открытого обсуждения %{total}"
 | 
			
		||||
 | 
			
		||||
msgid "%{total} warnings found: showing first %{warningsDisplayed}"
 | 
			
		||||
msgstr ""
 | 
			
		||||
| 
						 | 
				
			
			@ -1258,7 +1258,7 @@ msgstr[2] "- Пользователи"
 | 
			
		|||
msgstr[3] "- Пользователи"
 | 
			
		||||
 | 
			
		||||
msgid "- of - weight completed"
 | 
			
		||||
msgstr "- из - приоритета завершено"
 | 
			
		||||
msgstr "- из - единиц веса завершено"
 | 
			
		||||
 | 
			
		||||
msgid "- show less"
 | 
			
		||||
msgstr "- свернуть"
 | 
			
		||||
| 
						 | 
				
			
			@ -3448,7 +3448,7 @@ msgid "An error occurred when updating the issue title"
 | 
			
		|||
msgstr ""
 | 
			
		||||
 | 
			
		||||
msgid "An error occurred when updating the issue weight"
 | 
			
		||||
msgstr "Произошла ошибка при обновлении приоритета обсуждения"
 | 
			
		||||
msgstr "Произошла ошибка при обновлении весa обсуждения"
 | 
			
		||||
 | 
			
		||||
msgid "An error occurred while acknowledging the notification. Refresh the page and try again."
 | 
			
		||||
msgstr ""
 | 
			
		||||
| 
						 | 
				
			
			@ -5352,7 +5352,7 @@ msgid "Burndown charts are now fixed. This means that removing issues from a mil
 | 
			
		|||
msgstr "Диаграммы сгорания задач стали фиксированными. Это означает, что если убрать обсуждения из этапа, ставшего просроченным, диаграмма не изменится. Прежний вариант диаграммы можно увидеть, нажав на кнопку %{strongStart}Старый вид диаграммы сгорания%{strongEnd}."
 | 
			
		||||
 | 
			
		||||
msgid "BurndownChartLabel|Open issue weight"
 | 
			
		||||
msgstr "Открыть приоритет задачи"
 | 
			
		||||
msgstr "Bес открытых обсуждений"
 | 
			
		||||
 | 
			
		||||
msgid "BurndownChartLabel|Open issues"
 | 
			
		||||
msgstr "Открыть обсуждения"
 | 
			
		||||
| 
						 | 
				
			
			@ -6346,13 +6346,13 @@ msgid "Clear templates search input"
 | 
			
		|||
msgstr "Очистить шаблоны ввода данных для поиска"
 | 
			
		||||
 | 
			
		||||
msgid "Clear weight"
 | 
			
		||||
msgstr "Очистить приоритет"
 | 
			
		||||
msgstr "Удалить вес"
 | 
			
		||||
 | 
			
		||||
msgid "Cleared weight."
 | 
			
		||||
msgstr "Приоритет очищен."
 | 
			
		||||
msgstr "Вес удалён."
 | 
			
		||||
 | 
			
		||||
msgid "Clears weight."
 | 
			
		||||
msgstr "Очищает приоритет."
 | 
			
		||||
msgstr "Удаляет вес."
 | 
			
		||||
 | 
			
		||||
msgid "Click %{link_start}here%{link_end} to view the request."
 | 
			
		||||
msgstr ""
 | 
			
		||||
| 
						 | 
				
			
			@ -11728,7 +11728,7 @@ msgid "Enter the number of seconds, or other human-readable input, like \"1 hour
 | 
			
		|||
msgstr ""
 | 
			
		||||
 | 
			
		||||
msgid "Enter weights for storages for new repositories."
 | 
			
		||||
msgstr ""
 | 
			
		||||
msgstr "Введите веса хранилищ для новых репозиториев."
 | 
			
		||||
 | 
			
		||||
msgid "Enter your password to approve"
 | 
			
		||||
msgstr "Для продолжения введите свой пароль"
 | 
			
		||||
| 
						 | 
				
			
			@ -12244,7 +12244,7 @@ msgid "Error occurred while updating the issue status"
 | 
			
		|||
msgstr "Произошла ошибка при обновлении статуса обсуждения"
 | 
			
		||||
 | 
			
		||||
msgid "Error occurred while updating the issue weight"
 | 
			
		||||
msgstr "Произошла ошибка при изменении приоритета задачи"
 | 
			
		||||
msgstr "Произошла ошибка при изменении весa обсуждения"
 | 
			
		||||
 | 
			
		||||
msgid "Error occurred. A blocked user cannot be deactivated"
 | 
			
		||||
msgstr "Произошла ошибка. Заблокированный пользователь не может быть деактивирован"
 | 
			
		||||
| 
						 | 
				
			
			@ -17099,7 +17099,7 @@ msgid "Issue was closed by %{name} %{reason}"
 | 
			
		|||
msgstr "Обсуждение было закрыто %{name} %{reason}"
 | 
			
		||||
 | 
			
		||||
msgid "Issue weight"
 | 
			
		||||
msgstr "Приоритет обсуждения"
 | 
			
		||||
msgstr "Вес обсуждения"
 | 
			
		||||
 | 
			
		||||
msgid "IssueAnalytics|Age"
 | 
			
		||||
msgstr ""
 | 
			
		||||
| 
						 | 
				
			
			@ -24622,10 +24622,10 @@ msgid "Promotions|Weight"
 | 
			
		|||
msgstr ""
 | 
			
		||||
 | 
			
		||||
msgid "Promotions|Weighting your issue"
 | 
			
		||||
msgstr "Обозначить приоритет обсуждения"
 | 
			
		||||
msgstr "Установка веса обсуждения"
 | 
			
		||||
 | 
			
		||||
msgid "Promotions|When you have a lot of issues, it can be hard to get an overview. By adding a weight to your issues, you can get a better idea of the effort, cost, required time, or value of each, and so better manage them."
 | 
			
		||||
msgstr "При большом количестве обсуждений сложно оценить общую картину. Добавив приоритет к вашим обсуждениям, вы можете получить лучшее представление о затрачиваемых усилиях, времени, стоимости или значимости каждого из них и, таким образом, лучше ими управлять."
 | 
			
		||||
msgstr "При большом количестве обсуждений сложно оценить общую картину. Добавив вес к вашим обсуждениям, вы можете получить лучшее представление о затрачиваемых усилиях, времени, стоимости или значимости каждого из них и, таким образом, лучше ими управлять."
 | 
			
		||||
 | 
			
		||||
msgid "Promotions|With Contribution Analytics you can have an overview for the activity of issues, merge requests, and push events of your organization and its members."
 | 
			
		||||
msgstr ""
 | 
			
		||||
| 
						 | 
				
			
			@ -27685,10 +27685,10 @@ msgid "Set verification limit and frequency."
 | 
			
		|||
msgstr ""
 | 
			
		||||
 | 
			
		||||
msgid "Set weight"
 | 
			
		||||
msgstr "Установить приоритет"
 | 
			
		||||
msgstr "Установить вес"
 | 
			
		||||
 | 
			
		||||
msgid "Set weight to %{weight}."
 | 
			
		||||
msgstr "Установить приоритет на %{weight}."
 | 
			
		||||
msgstr "Установить вес в следующее количество единиц: %{weight}."
 | 
			
		||||
 | 
			
		||||
msgid "Set what should be replicated by this secondary node."
 | 
			
		||||
msgstr ""
 | 
			
		||||
| 
						 | 
				
			
			@ -27748,7 +27748,7 @@ msgid "Sets time estimate to %{time_estimate}."
 | 
			
		|||
msgstr "Задаёт оценку времени в %{time_estimate}."
 | 
			
		||||
 | 
			
		||||
msgid "Sets weight to %{weight}."
 | 
			
		||||
msgstr "Устанавливает приоритет на %{weight}."
 | 
			
		||||
msgstr "Устанавливает вес в следующее количество единиц: %{weight}."
 | 
			
		||||
 | 
			
		||||
msgid "Setting this to 0 means using the system default timeout value."
 | 
			
		||||
msgstr ""
 | 
			
		||||
| 
						 | 
				
			
			@ -27975,7 +27975,7 @@ msgid "Sidebar|Only numeral characters allowed"
 | 
			
		|||
msgstr ""
 | 
			
		||||
 | 
			
		||||
msgid "Sidebar|Weight"
 | 
			
		||||
msgstr "Приоритет"
 | 
			
		||||
msgstr "Вес"
 | 
			
		||||
 | 
			
		||||
msgid "Sign in"
 | 
			
		||||
msgstr "Вход"
 | 
			
		||||
| 
						 | 
				
			
			@ -28458,7 +28458,7 @@ msgid "SortOptions|Least popular"
 | 
			
		|||
msgstr "Наименее популярный"
 | 
			
		||||
 | 
			
		||||
msgid "SortOptions|Less weight"
 | 
			
		||||
msgstr "Сначала с меньшим приоритетом"
 | 
			
		||||
msgstr "Сначала с меньшим весом"
 | 
			
		||||
 | 
			
		||||
msgid "SortOptions|Manual"
 | 
			
		||||
msgstr "Ручная"
 | 
			
		||||
| 
						 | 
				
			
			@ -28473,7 +28473,7 @@ msgid "SortOptions|Milestone due soon"
 | 
			
		|||
msgstr "Веха, наступающая раньше"
 | 
			
		||||
 | 
			
		||||
msgid "SortOptions|More weight"
 | 
			
		||||
msgstr "Сначала с большим приоритетом"
 | 
			
		||||
msgstr "Сначала с большим весом"
 | 
			
		||||
 | 
			
		||||
msgid "SortOptions|Most popular"
 | 
			
		||||
msgstr "Наиболее популярный"
 | 
			
		||||
| 
						 | 
				
			
			@ -28554,7 +28554,7 @@ msgid "SortOptions|Version"
 | 
			
		|||
msgstr "По версии"
 | 
			
		||||
 | 
			
		||||
msgid "SortOptions|Weight"
 | 
			
		||||
msgstr "Приоритет"
 | 
			
		||||
msgstr "Вес"
 | 
			
		||||
 | 
			
		||||
msgid "Source"
 | 
			
		||||
msgstr "Источник"
 | 
			
		||||
| 
						 | 
				
			
			@ -31798,7 +31798,7 @@ msgid "Total users"
 | 
			
		|||
msgstr ""
 | 
			
		||||
 | 
			
		||||
msgid "Total weight"
 | 
			
		||||
msgstr "Итоговый приоритет"
 | 
			
		||||
msgstr "Общий вес"
 | 
			
		||||
 | 
			
		||||
msgid "Total: %{total}"
 | 
			
		||||
msgstr "Всего: %{total}"
 | 
			
		||||
| 
						 | 
				
			
			@ -33976,10 +33976,10 @@ msgid "Weeks"
 | 
			
		|||
msgstr "Недели"
 | 
			
		||||
 | 
			
		||||
msgid "Weight"
 | 
			
		||||
msgstr "Приоритет"
 | 
			
		||||
msgstr "Вес"
 | 
			
		||||
 | 
			
		||||
msgid "Weight %{weight}"
 | 
			
		||||
msgstr "Приоритет %{weight}"
 | 
			
		||||
msgstr "Вес %{weight}"
 | 
			
		||||
 | 
			
		||||
msgid "Welcome back! Your account had been deactivated due to inactivity but is now reactivated."
 | 
			
		||||
msgstr "Добро пожаловать! Ваша учётная запись была деактивирована из-за неактивности, но сейчас восстановлена."
 | 
			
		||||
| 
						 | 
				
			
			@ -36507,7 +36507,7 @@ msgid "remove due date"
 | 
			
		|||
msgstr "удалить дату завершения"
 | 
			
		||||
 | 
			
		||||
msgid "remove weight"
 | 
			
		||||
msgstr "удалить приоритет"
 | 
			
		||||
msgstr "удалить вес"
 | 
			
		||||
 | 
			
		||||
msgid "removed a Zoom call from this issue"
 | 
			
		||||
msgstr ""
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,45 +1,47 @@
 | 
			
		|||
import Vue from 'vue';
 | 
			
		||||
import mountComponent from 'helpers/vue_mount_component_helper';
 | 
			
		||||
import banner from '~/cycle_analytics/components/banner.vue';
 | 
			
		||||
import { shallowMount } from '@vue/test-utils';
 | 
			
		||||
import Banner from '~/cycle_analytics/components/banner.vue';
 | 
			
		||||
 | 
			
		||||
describe('Value Stream Analytics banner', () => {
 | 
			
		||||
  let vm;
 | 
			
		||||
  let wrapper;
 | 
			
		||||
 | 
			
		||||
  const createComponent = () => {
 | 
			
		||||
    wrapper = shallowMount(Banner, {
 | 
			
		||||
      propsData: {
 | 
			
		||||
        documentationLink: 'path',
 | 
			
		||||
      },
 | 
			
		||||
    });
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  beforeEach(() => {
 | 
			
		||||
    const Component = Vue.extend(banner);
 | 
			
		||||
    vm = mountComponent(Component, {
 | 
			
		||||
      documentationLink: 'path',
 | 
			
		||||
    });
 | 
			
		||||
    createComponent();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  afterEach(() => {
 | 
			
		||||
    vm.$destroy();
 | 
			
		||||
    wrapper.destroy();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should render value stream analytics information', () => {
 | 
			
		||||
    expect(vm.$el.querySelector('h4').textContent.trim()).toEqual(
 | 
			
		||||
      'Introducing Value Stream Analytics',
 | 
			
		||||
    );
 | 
			
		||||
    expect(wrapper.find('h4').text().trim()).toBe('Introducing Value Stream Analytics');
 | 
			
		||||
 | 
			
		||||
    expect(
 | 
			
		||||
      vm.$el
 | 
			
		||||
        .querySelector('p')
 | 
			
		||||
        .textContent.trim()
 | 
			
		||||
      wrapper
 | 
			
		||||
        .find('p')
 | 
			
		||||
        .text()
 | 
			
		||||
        .trim()
 | 
			
		||||
        .replace(/[\r\n]+/g, ' '),
 | 
			
		||||
    ).toContain(
 | 
			
		||||
      'Value Stream Analytics gives an overview of how much time it takes to go from idea to production in your project.',
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    expect(vm.$el.querySelector('a').textContent.trim()).toEqual('Read more');
 | 
			
		||||
 | 
			
		||||
    expect(vm.$el.querySelector('a').getAttribute('href')).toEqual('path');
 | 
			
		||||
    expect(wrapper.find('a').text().trim()).toBe('Read more');
 | 
			
		||||
    expect(wrapper.find('a').attributes('href')).toBe('path');
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should emit an event when close button is clicked', () => {
 | 
			
		||||
    jest.spyOn(vm, '$emit').mockImplementation(() => {});
 | 
			
		||||
  it('should emit an event when close button is clicked', async () => {
 | 
			
		||||
    jest.spyOn(wrapper.vm, '$emit').mockImplementation(() => {});
 | 
			
		||||
 | 
			
		||||
    vm.$el.querySelector('.js-ca-dismiss-button').click();
 | 
			
		||||
    await wrapper.find('.js-ca-dismiss-button').trigger('click');
 | 
			
		||||
 | 
			
		||||
    expect(vm.$emit).toHaveBeenCalled();
 | 
			
		||||
    expect(wrapper.vm.$emit).toHaveBeenCalled();
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -187,6 +187,21 @@ RSpec.describe Gitlab::ObjectHierarchy do
 | 
			
		|||
  context 'when the use_distinct_in_object_hierarchy feature flag is enabled' do
 | 
			
		||||
    before do
 | 
			
		||||
      stub_feature_flags(use_distinct_in_object_hierarchy: true)
 | 
			
		||||
      stub_feature_flags(use_distinct_for_all_object_hierarchy: false)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it_behaves_like 'Gitlab::ObjectHierarchy test cases'
 | 
			
		||||
 | 
			
		||||
    it 'calls DISTINCT' do
 | 
			
		||||
      expect(parent.self_and_descendants.to_sql).to include("DISTINCT")
 | 
			
		||||
      expect(child2.self_and_ancestors.to_sql).to include("DISTINCT")
 | 
			
		||||
    end
 | 
			
		||||
  end
 | 
			
		||||
 | 
			
		||||
  context 'when the use_distinct_for_all_object_hierarchy feature flag is enabled' do
 | 
			
		||||
    before do
 | 
			
		||||
      stub_feature_flags(use_distinct_in_object_hierarchy: false)
 | 
			
		||||
      stub_feature_flags(use_distinct_for_all_object_hierarchy: true)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it_behaves_like 'Gitlab::ObjectHierarchy test cases'
 | 
			
		||||
| 
						 | 
				
			
			@ -200,6 +215,7 @@ RSpec.describe Gitlab::ObjectHierarchy do
 | 
			
		|||
  context 'when the use_distinct_in_object_hierarchy feature flag is disabled' do
 | 
			
		||||
    before do
 | 
			
		||||
      stub_feature_flags(use_distinct_in_object_hierarchy: false)
 | 
			
		||||
      stub_feature_flags(use_distinct_for_all_object_hierarchy: false)
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    it_behaves_like 'Gitlab::ObjectHierarchy test cases'
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -107,7 +107,7 @@ RSpec.describe API::API do
 | 
			
		|||
      allow_any_instance_of(Gitlab::GrapeLogging::Loggers::ContextLogger).to receive(:parameters) do
 | 
			
		||||
        Gitlab::ApplicationContext.current.tap do |log_context|
 | 
			
		||||
          expect(log_context).to match('correlation_id' => an_instance_of(String),
 | 
			
		||||
                                       'meta.caller_id' => '/api/:version/projects/:id/issues',
 | 
			
		||||
                                       'meta.caller_id' => 'GET /api/:version/projects/:id/issues',
 | 
			
		||||
                                       'meta.remote_ip' => an_instance_of(String),
 | 
			
		||||
                                       'meta.project' => project.full_path,
 | 
			
		||||
                                       'meta.root_namespace' => project.namespace.full_path,
 | 
			
		||||
| 
						 | 
				
			
			@ -124,7 +124,7 @@ RSpec.describe API::API do
 | 
			
		|||
      allow_any_instance_of(Gitlab::GrapeLogging::Loggers::ContextLogger).to receive(:parameters) do
 | 
			
		||||
        Gitlab::ApplicationContext.current.tap do |log_context|
 | 
			
		||||
          expect(log_context).to match('correlation_id' => an_instance_of(String),
 | 
			
		||||
                                       'meta.caller_id' => '/api/:version/users',
 | 
			
		||||
                                       'meta.caller_id' => 'GET /api/:version/users',
 | 
			
		||||
                                       'meta.remote_ip' => an_instance_of(String),
 | 
			
		||||
                                       'meta.client_id' => an_instance_of(String),
 | 
			
		||||
                                       'meta.feature_category' => 'users')
 | 
			
		||||
| 
						 | 
				
			
			@ -141,7 +141,7 @@ RSpec.describe API::API do
 | 
			
		|||
      let(:component_map) do
 | 
			
		||||
        {
 | 
			
		||||
          "application" => "test",
 | 
			
		||||
          "endpoint_id" => "/api/:version/users/:id"
 | 
			
		||||
          "endpoint_id" => "GET /api/:version/users/:id"
 | 
			
		||||
        }
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -127,7 +127,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
 | 
			
		|||
              authorize_artifacts_with_token_in_params
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            it_behaves_like 'API::CI::Runner application context metadata', '/api/:version/jobs/:id/artifacts/authorize' do
 | 
			
		||||
            it_behaves_like 'API::CI::Runner application context metadata', 'POST /api/:version/jobs/:id/artifacts/authorize' do
 | 
			
		||||
              let(:send_request) { subject }
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -262,7 +262,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
 | 
			
		|||
      end
 | 
			
		||||
 | 
			
		||||
      describe 'POST /api/v4/jobs/:id/artifacts' do
 | 
			
		||||
        it_behaves_like 'API::CI::Runner application context metadata', '/api/:version/jobs/:id/artifacts' do
 | 
			
		||||
        it_behaves_like 'API::CI::Runner application context metadata', 'POST /api/:version/jobs/:id/artifacts' do
 | 
			
		||||
          let(:send_request) do
 | 
			
		||||
            upload_artifacts(file_upload, headers_with_token)
 | 
			
		||||
          end
 | 
			
		||||
| 
						 | 
				
			
			@ -784,7 +784,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
 | 
			
		|||
      describe 'GET /api/v4/jobs/:id/artifacts' do
 | 
			
		||||
        let(:token) { job.token }
 | 
			
		||||
 | 
			
		||||
        it_behaves_like 'API::CI::Runner application context metadata', '/api/:version/jobs/:id/artifacts' do
 | 
			
		||||
        it_behaves_like 'API::CI::Runner application context metadata', 'GET /api/:version/jobs/:id/artifacts' do
 | 
			
		||||
          let(:send_request) { download_artifact }
 | 
			
		||||
        end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -36,7 +36,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
 | 
			
		|||
        job.run!
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it_behaves_like 'API::CI::Runner application context metadata', '/api/:version/jobs/:id' do
 | 
			
		||||
      it_behaves_like 'API::CI::Runner application context metadata', 'PUT /api/:version/jobs/:id' do
 | 
			
		||||
        let(:send_request) { update_job(state: 'success') }
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,7 +41,7 @@ RSpec.describe API::Ci::Runner, :clean_gitlab_redis_shared_state do
 | 
			
		|||
        initial_patch_the_trace
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it_behaves_like 'API::CI::Runner application context metadata', '/api/:version/jobs/:id/trace' do
 | 
			
		||||
      it_behaves_like 'API::CI::Runner application context metadata', 'PATCH /api/:version/jobs/:id/trace' do
 | 
			
		||||
        let(:send_request) { patch_the_trace }
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -644,7 +644,7 @@ RSpec.describe API::Internal::Base do
 | 
			
		|||
 | 
			
		||||
      context 'with Project' do
 | 
			
		||||
        it_behaves_like 'storing arguments in the application context' do
 | 
			
		||||
          let(:expected_params) { { user: key.user.username, project: project.full_path } }
 | 
			
		||||
          let(:expected_params) { { user: key.user.username, project: project.full_path, caller_id: "POST /api/:version/internal/allowed" } }
 | 
			
		||||
 | 
			
		||||
          subject { push(key, project) }
 | 
			
		||||
        end
 | 
			
		||||
| 
						 | 
				
			
			@ -652,7 +652,7 @@ RSpec.describe API::Internal::Base do
 | 
			
		|||
 | 
			
		||||
      context 'with PersonalSnippet' do
 | 
			
		||||
        it_behaves_like 'storing arguments in the application context' do
 | 
			
		||||
          let(:expected_params) { { user: key.user.username } }
 | 
			
		||||
          let(:expected_params) { { user: key.user.username, caller_id: "POST /api/:version/internal/allowed" } }
 | 
			
		||||
 | 
			
		||||
          subject { push(key, personal_snippet) }
 | 
			
		||||
        end
 | 
			
		||||
| 
						 | 
				
			
			@ -660,7 +660,7 @@ RSpec.describe API::Internal::Base do
 | 
			
		|||
 | 
			
		||||
      context 'with ProjectSnippet' do
 | 
			
		||||
        it_behaves_like 'storing arguments in the application context' do
 | 
			
		||||
          let(:expected_params) { { user: key.user.username, project: project_snippet.project.full_path } }
 | 
			
		||||
          let(:expected_params) { { user: key.user.username, project: project_snippet.project.full_path, caller_id: "POST /api/:version/internal/allowed" } }
 | 
			
		||||
 | 
			
		||||
          subject { push(key, project_snippet) }
 | 
			
		||||
        end
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -228,6 +228,7 @@ module Ci
 | 
			
		|||
          context 'when the use_distinct_in_register_job_object_hierarchy feature flag is enabled' do
 | 
			
		||||
            before do
 | 
			
		||||
              stub_feature_flags(use_distinct_in_register_job_object_hierarchy: true)
 | 
			
		||||
              stub_feature_flags(use_distinct_for_all_object_hierarchy: true)
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            it 'calls DISTINCT' do
 | 
			
		||||
| 
						 | 
				
			
			@ -238,6 +239,7 @@ module Ci
 | 
			
		|||
          context 'when the use_distinct_in_register_job_object_hierarchy feature flag is disabled' do
 | 
			
		||||
            before do
 | 
			
		||||
              stub_feature_flags(use_distinct_in_register_job_object_hierarchy: false)
 | 
			
		||||
              stub_feature_flags(use_distinct_for_all_object_hierarchy: false)
 | 
			
		||||
            end
 | 
			
		||||
 | 
			
		||||
            it 'does not call DISTINCT' do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,7 +25,7 @@ RSpec.shared_examples 'can highlight results' do |search_term|
 | 
			
		|||
  end
 | 
			
		||||
 | 
			
		||||
  it 'highlights the search terms' do
 | 
			
		||||
    selector = '.gl-bg-orange-50'
 | 
			
		||||
    selector = '.gl-bg-orange-100'
 | 
			
		||||
    fill_in SearchHelpers::INPUT_PLACEHOLDER, with: search_term
 | 
			
		||||
 | 
			
		||||
    expect(page).to have_css(selector)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,6 +29,15 @@ RSpec.describe RemoveExpiredMembersWorker do
 | 
			
		|||
        worker.perform
 | 
			
		||||
        expect(non_expiring_project_member.reload).to be_present
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'adds context to resulting jobs' do
 | 
			
		||||
        worker.perform
 | 
			
		||||
 | 
			
		||||
        new_job = Sidekiq::Worker.jobs.last
 | 
			
		||||
 | 
			
		||||
        expect(new_job).to include('meta.project' => expired_project_member.project.full_path,
 | 
			
		||||
                                   'meta.user' => expired_project_member.user.username)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'project bots' do
 | 
			
		||||
| 
						 | 
				
			
			@ -98,6 +107,15 @@ RSpec.describe RemoveExpiredMembersWorker do
 | 
			
		|||
        worker.perform
 | 
			
		||||
        expect(non_expiring_group_member.reload).to be_present
 | 
			
		||||
      end
 | 
			
		||||
 | 
			
		||||
      it 'adds context to resulting jobs' do
 | 
			
		||||
        worker.perform
 | 
			
		||||
 | 
			
		||||
        new_job = Sidekiq::Worker.jobs.last
 | 
			
		||||
 | 
			
		||||
        expect(new_job).to include('meta.root_namespace' => expired_group_member.group.full_path,
 | 
			
		||||
                                   'meta.user' => expired_group_member.user.username)
 | 
			
		||||
      end
 | 
			
		||||
    end
 | 
			
		||||
 | 
			
		||||
    context 'when the last group owner expires' do
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ RUN bundle config --global frozen 1
 | 
			
		|||
 | 
			
		||||
WORKDIR /usr/src/app
 | 
			
		||||
 | 
			
		||||
COPY Gemfile Gemfile.lock .
 | 
			
		||||
COPY Gemfile Gemfile.lock /usr/src/app/
 | 
			
		||||
# Install build dependencies - required for gems with native dependencies
 | 
			
		||||
RUN apk add --no-cache --virtual build-deps build-base postgresql-dev && \
 | 
			
		||||
  bundle install && \
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue