Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									45999bfdec
								
							
						
					
					
						commit
						c2908ec6a0
					
				|  | @ -3,7 +3,7 @@ | |||
| import AccessorUtilities from './lib/utils/accessor'; | ||||
| 
 | ||||
| export default class Autosave { | ||||
|   constructor(field, key, fallbackKey) { | ||||
|   constructor(field, key, fallbackKey, lockVersion) { | ||||
|     this.field = field; | ||||
| 
 | ||||
|     this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); | ||||
|  | @ -12,6 +12,8 @@ export default class Autosave { | |||
|     } | ||||
|     this.key = `autosave/${key}`; | ||||
|     this.fallbackKey = fallbackKey; | ||||
|     this.lockVersionKey = `${this.key}/lockVersion`; | ||||
|     this.lockVersion = lockVersion; | ||||
|     this.field.data('autosave', this); | ||||
|     this.restore(); | ||||
|     this.field.on('input', () => this.save()); | ||||
|  | @ -40,6 +42,11 @@ export default class Autosave { | |||
|     } | ||||
|   } | ||||
| 
 | ||||
|   getSavedLockVersion() { | ||||
|     if (!this.isLocalStorageAvailable) return; | ||||
|     return window.localStorage.getItem(this.lockVersionKey); | ||||
|   } | ||||
| 
 | ||||
|   save() { | ||||
|     if (!this.field.length) return; | ||||
| 
 | ||||
|  | @ -49,6 +56,9 @@ export default class Autosave { | |||
|       if (this.fallbackKey) { | ||||
|         window.localStorage.setItem(this.fallbackKey, text); | ||||
|       } | ||||
|       if (this.lockVersion !== undefined) { | ||||
|         window.localStorage.setItem(this.lockVersionKey, this.lockVersion); | ||||
|       } | ||||
|       return window.localStorage.setItem(this.key, text); | ||||
|     } | ||||
| 
 | ||||
|  | @ -58,6 +68,7 @@ export default class Autosave { | |||
|   reset() { | ||||
|     if (!this.isLocalStorageAvailable) return; | ||||
| 
 | ||||
|     window.localStorage.removeItem(this.lockVersionKey); | ||||
|     window.localStorage.removeItem(this.fallbackKey); | ||||
|     return window.localStorage.removeItem(this.key); | ||||
|   } | ||||
|  |  | |||
|  | @ -75,6 +75,7 @@ export default { | |||
|         <template #actions> | ||||
|           <clone-dropdown-button | ||||
|             v-if="canBeCloned" | ||||
|             class="mr-2" | ||||
|             :ssh-link="snippet.sshUrlToRepo" | ||||
|             :http-link="snippet.httpUrlToRepo" | ||||
|           /> | ||||
|  |  | |||
|  | @ -1,14 +1,14 @@ | |||
| import Vue from 'vue'; | ||||
| import { parseBoolean } from '~/lib/utils/common_utils'; | ||||
| import StaticSiteEditor from './components/static_site_editor.vue'; | ||||
| import createStore from './store'; | ||||
| 
 | ||||
| const initStaticSiteEditor = el => { | ||||
|   const { projectId, path: sourcePath, returnUrl } = el.dataset; | ||||
|   const isSupportedContent = 'isSupportedContent' in el.dataset; | ||||
|   const { isSupportedContent, projectId, path: sourcePath, returnUrl } = el.dataset; | ||||
| 
 | ||||
|   const store = createStore({ | ||||
|     initialState: { | ||||
|       isSupportedContent, | ||||
|       isSupportedContent: parseBoolean(isSupportedContent), | ||||
|       projectId, | ||||
|       returnUrl, | ||||
|       sourcePath, | ||||
|  |  | |||
|  | @ -12,7 +12,7 @@ class Projects::RefsController < Projects::ApplicationController | |||
|   before_action :authorize_download_code! | ||||
| 
 | ||||
|   before_action only: [:logs_tree] do | ||||
|     push_frontend_feature_flag(:vue_file_list_lfs_badge) | ||||
|     push_frontend_feature_flag(:vue_file_list_lfs_badge, default_enabled: true) | ||||
|   end | ||||
| 
 | ||||
|   def switch | ||||
|  |  | |||
|  | @ -16,7 +16,7 @@ class Projects::TreeController < Projects::ApplicationController | |||
|   before_action :authorize_edit_tree!, only: [:create_dir] | ||||
| 
 | ||||
|   before_action only: [:show] do | ||||
|     push_frontend_feature_flag(:vue_file_list_lfs_badge) | ||||
|     push_frontend_feature_flag(:vue_file_list_lfs_badge, default_enabled: true) | ||||
|   end | ||||
| 
 | ||||
|   def show | ||||
|  |  | |||
|  | @ -25,4 +25,8 @@ class ResourceMilestoneEvent < ResourceEvent | |||
|   def self.issuable_attrs | ||||
|     %i(issue merge_request).freeze | ||||
|   end | ||||
| 
 | ||||
|   def milestone_title | ||||
|     milestone&.title | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -67,22 +67,30 @@ module Issuable | |||
|       end | ||||
| 
 | ||||
|       def copy_resource_milestone_events | ||||
|         entity_key = new_entity.class.name.underscore.foreign_key | ||||
|         return unless milestone_events_supported? | ||||
| 
 | ||||
|         copy_events(ResourceMilestoneEvent.table_name, original_entity.resource_milestone_events) do |event| | ||||
|           matching_destination_milestone = matching_milestone(event.milestone.title) | ||||
|           if event.remove? | ||||
|             event_attributes_with_milestone(event, nil) | ||||
|           else | ||||
|             matching_destination_milestone = matching_milestone(event.milestone_title) | ||||
| 
 | ||||
|           if matching_destination_milestone.present? | ||||
|             event.attributes | ||||
|               .except('id') | ||||
|               .merge(entity_key => new_entity.id, | ||||
|                      'milestone_id' => matching_destination_milestone.id, | ||||
|                      'action' => ResourceMilestoneEvent.actions[event.action], | ||||
|                      'state' => ResourceMilestoneEvent.states[event.state]) | ||||
|             event_attributes_with_milestone(event, matching_destination_milestone) if matching_destination_milestone.present? | ||||
|           end | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       def event_attributes_with_milestone(event, milestone) | ||||
|         entity_key = new_entity.class.name.underscore.foreign_key | ||||
| 
 | ||||
|         event.attributes | ||||
|           .except('id') | ||||
|           .merge(entity_key => new_entity.id, | ||||
|                  'milestone_id' => milestone&.id, | ||||
|                  'action' => ResourceMilestoneEvent.actions[event.action], | ||||
|                  'state' => ResourceMilestoneEvent.states[event.state]) | ||||
|       end | ||||
| 
 | ||||
|       def copy_events(table_name, events_to_copy) | ||||
|         events_to_copy.find_in_batches do |batch| | ||||
|           events = batch.map do |event| | ||||
|  | @ -96,6 +104,11 @@ module Issuable | |||
|       def entity_key | ||||
|         new_entity.class.name.parameterize('_').foreign_key | ||||
|       end | ||||
| 
 | ||||
|       def milestone_events_supported? | ||||
|         original_entity.respond_to?(:resource_milestone_events) && | ||||
|           new_entity.respond_to?(:resource_milestone_events) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Create operations_strategies_user_lists table | ||||
| merge_request: 30243 | ||||
| author: | ||||
| type: added | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Added right margin to Clone Snippet button | ||||
| merge_request: 30471 | ||||
| author: | ||||
| type: fixed | ||||
|  | @ -0,0 +1,14 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class CreateOperationsStrategiesUserLists < ActiveRecord::Migration[6.0] | ||||
|   DOWNTIME = false | ||||
| 
 | ||||
|   def change | ||||
|     create_table :operations_strategies_user_lists do |t| | ||||
|       t.references :strategy, index: false, foreign_key: { on_delete: :cascade, to_table: :operations_strategies }, null: false | ||||
|       t.references :user_list, index: true, foreign_key: { on_delete: :cascade, to_table: :operations_user_lists }, null: false | ||||
| 
 | ||||
|       t.index [:strategy_id, :user_list_id], unique: true, name: :index_ops_strategies_user_lists_on_strategy_id_and_user_list_id | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -4415,6 +4415,21 @@ CREATE SEQUENCE public.operations_strategies_id_seq | |||
| 
 | ||||
| ALTER SEQUENCE public.operations_strategies_id_seq OWNED BY public.operations_strategies.id; | ||||
| 
 | ||||
| CREATE TABLE public.operations_strategies_user_lists ( | ||||
|     id bigint NOT NULL, | ||||
|     strategy_id bigint NOT NULL, | ||||
|     user_list_id bigint NOT NULL | ||||
| ); | ||||
| 
 | ||||
| CREATE SEQUENCE public.operations_strategies_user_lists_id_seq | ||||
|     START WITH 1 | ||||
|     INCREMENT BY 1 | ||||
|     NO MINVALUE | ||||
|     NO MAXVALUE | ||||
|     CACHE 1; | ||||
| 
 | ||||
| ALTER SEQUENCE public.operations_strategies_user_lists_id_seq OWNED BY public.operations_strategies_user_lists.id; | ||||
| 
 | ||||
| CREATE TABLE public.operations_user_lists ( | ||||
|     id bigint NOT NULL, | ||||
|     project_id bigint NOT NULL, | ||||
|  | @ -7489,6 +7504,8 @@ ALTER TABLE ONLY public.operations_scopes ALTER COLUMN id SET DEFAULT nextval('p | |||
| 
 | ||||
| ALTER TABLE ONLY public.operations_strategies ALTER COLUMN id SET DEFAULT nextval('public.operations_strategies_id_seq'::regclass); | ||||
| 
 | ||||
| ALTER TABLE ONLY public.operations_strategies_user_lists ALTER COLUMN id SET DEFAULT nextval('public.operations_strategies_user_lists_id_seq'::regclass); | ||||
| 
 | ||||
| ALTER TABLE ONLY public.operations_user_lists ALTER COLUMN id SET DEFAULT nextval('public.operations_user_lists_id_seq'::regclass); | ||||
| 
 | ||||
| ALTER TABLE ONLY public.packages_build_infos ALTER COLUMN id SET DEFAULT nextval('public.packages_build_infos_id_seq'::regclass); | ||||
|  | @ -8314,6 +8331,9 @@ ALTER TABLE ONLY public.operations_scopes | |||
| ALTER TABLE ONLY public.operations_strategies | ||||
|     ADD CONSTRAINT operations_strategies_pkey PRIMARY KEY (id); | ||||
| 
 | ||||
| ALTER TABLE ONLY public.operations_strategies_user_lists | ||||
|     ADD CONSTRAINT operations_strategies_user_lists_pkey PRIMARY KEY (id); | ||||
| 
 | ||||
| ALTER TABLE ONLY public.operations_user_lists | ||||
|     ADD CONSTRAINT operations_user_lists_pkey PRIMARY KEY (id); | ||||
| 
 | ||||
|  | @ -9923,10 +9943,14 @@ CREATE UNIQUE INDEX index_operations_scopes_on_strategy_id_and_environment_scope | |||
| 
 | ||||
| CREATE INDEX index_operations_strategies_on_feature_flag_id ON public.operations_strategies USING btree (feature_flag_id); | ||||
| 
 | ||||
| CREATE INDEX index_operations_strategies_user_lists_on_user_list_id ON public.operations_strategies_user_lists USING btree (user_list_id); | ||||
| 
 | ||||
| CREATE UNIQUE INDEX index_operations_user_lists_on_project_id_and_iid ON public.operations_user_lists USING btree (project_id, iid); | ||||
| 
 | ||||
| CREATE UNIQUE INDEX index_operations_user_lists_on_project_id_and_name ON public.operations_user_lists USING btree (project_id, name); | ||||
| 
 | ||||
| CREATE UNIQUE INDEX index_ops_strategies_user_lists_on_strategy_id_and_user_list_id ON public.operations_strategies_user_lists USING btree (strategy_id, user_list_id); | ||||
| 
 | ||||
| CREATE UNIQUE INDEX index_packages_build_infos_on_package_id ON public.packages_build_infos USING btree (package_id); | ||||
| 
 | ||||
| CREATE INDEX index_packages_build_infos_on_pipeline_id ON public.packages_build_infos USING btree (pipeline_id); | ||||
|  | @ -11584,6 +11608,9 @@ ALTER TABLE ONLY public.ci_resources | |||
| ALTER TABLE ONLY public.clusters_applications_fluentd | ||||
|     ADD CONSTRAINT fk_rails_4319b1dcd2 FOREIGN KEY (cluster_id) REFERENCES public.clusters(id) ON DELETE CASCADE; | ||||
| 
 | ||||
| ALTER TABLE ONLY public.operations_strategies_user_lists | ||||
|     ADD CONSTRAINT fk_rails_43241e8d29 FOREIGN KEY (strategy_id) REFERENCES public.operations_strategies(id) ON DELETE CASCADE; | ||||
| 
 | ||||
| ALTER TABLE ONLY public.lfs_file_locks | ||||
|     ADD CONSTRAINT fk_rails_43df7a0412 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE; | ||||
| 
 | ||||
|  | @ -12157,6 +12184,9 @@ ALTER TABLE ONLY public.ci_daily_report_results | |||
| ALTER TABLE ONLY public.issues_self_managed_prometheus_alert_events | ||||
|     ADD CONSTRAINT fk_rails_cc5d88bbb0 FOREIGN KEY (issue_id) REFERENCES public.issues(id) ON DELETE CASCADE; | ||||
| 
 | ||||
| ALTER TABLE ONLY public.operations_strategies_user_lists | ||||
|     ADD CONSTRAINT fk_rails_ccb7e4bc0b FOREIGN KEY (user_list_id) REFERENCES public.operations_user_lists(id) ON DELETE CASCADE; | ||||
| 
 | ||||
| ALTER TABLE ONLY public.issue_tracker_data | ||||
|     ADD CONSTRAINT fk_rails_ccc0840427 FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE CASCADE; | ||||
| 
 | ||||
|  | @ -13486,6 +13516,7 @@ COPY "schema_migrations" (version) FROM STDIN; | |||
| 20200420172927 | ||||
| 20200420201933 | ||||
| 20200421233150 | ||||
| 20200422213749 | ||||
| 20200423075720 | ||||
| 20200423080334 | ||||
| 20200423080607 | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ Updating Geo nodes involves performing: | |||
| Depending on which version of Geo you are updating to/from, there may be | ||||
| different steps. | ||||
| 
 | ||||
| - [Updating to GitLab 12.9](version_specific_updates.md#updating-to-gitlab-129) | ||||
| - [Updating to GitLab 12.7](version_specific_updates.md#updating-to-gitlab-127) | ||||
| - [Updating to GitLab 12.2](version_specific_updates.md#updating-to-gitlab-122) | ||||
| - [Updating to GitLab 12.1](version_specific_updates.md#updating-to-gitlab-121) | ||||
|  |  | |||
|  | @ -76,16 +76,16 @@ The connection settings match those provided by [Fog](https://github.com/fog), a | |||
| 
 | ||||
| | Setting | Description | Default | | ||||
| |---------|-------------|---------| | ||||
| | `provider` | Always `AWS` for compatible hosts | AWS | | ||||
| | `provider` | Always `AWS` for compatible hosts | `AWS` | | ||||
| | `aws_access_key_id` | AWS credentials, or compatible | | | ||||
| | `aws_secret_access_key` | AWS credentials, or compatible | | | ||||
| | `aws_signature_version` | AWS signature version to use. 2 or 4 are valid options. Digital Ocean Spaces and other providers may need 2. | 4 | | ||||
| | `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with [AWS v4 signatures](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be false | true | | ||||
| | `aws_signature_version` | AWS signature version to use. `2` or `4` are valid options. Digital Ocean Spaces and other providers may need `2`. | `4` | | ||||
| | `enable_signature_v4_streaming` | Set to true to enable HTTP chunked transfers with [AWS v4 signatures](https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-streaming.html). Oracle Cloud S3 needs this to be `false`. | `true` | | ||||
| | `region` | AWS region | us-east-1 | | ||||
| | `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | s3.amazonaws.com | | ||||
| | `host` | S3 compatible host for when not using AWS, e.g. `localhost` or `storage.example.com` | `s3.amazonaws.com` | | ||||
| | `endpoint` | Can be used when configuring an S3 compatible service such as [MinIO](https://min.io), by entering a URL such as `http://127.0.0.1:9000` | (optional) | | ||||
| | `path_style` | Set to true to use `host/bucket_name/object` style paths instead of `bucket_name.host/object`. Leave as false for AWS S3 | false | | ||||
| | `use_iam_profile` | Set to true to use IAM profile instead of access keys | false | ||||
| | `path_style` | Set to `true` to use `host/bucket_name/object` style paths instead of `bucket_name.host/object`. Leave as `false` for AWS S3. | `false` | | ||||
| | `use_iam_profile` | Set to `true` to use IAM profile instead of access keys | false | ||||
| 
 | ||||
| **In Omnibus installations:** | ||||
| 
 | ||||
|  | @ -149,8 +149,8 @@ Note that Oracle Cloud S3 must be sure to use the following settings: | |||
| 
 | ||||
| | Setting | Value | | ||||
| |---------|-------| | ||||
| | `enable_signature_v4_streaming` | false | | ||||
| | `path_style` | true | | ||||
| | `enable_signature_v4_streaming` | `false` | | ||||
| | `path_style` | `true` | | ||||
| 
 | ||||
| If `enable_signature_v4_streaming` is set to `true`, you may see the | ||||
| following error: | ||||
|  | @ -165,7 +165,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a | |||
| 
 | ||||
| | Setting | Description | Default | | ||||
| |---------|-------------|---------| | ||||
| | `provider` | Always `OpenStack` for compatible hosts | OpenStack | | ||||
| | `provider` | Always `OpenStack` for compatible hosts | `OpenStack` | | ||||
| | `openstack_username` | OpenStack username | | | ||||
| | `openstack_api_key` | OpenStack API key  | | | ||||
| | `openstack_temp_url_key` | OpenStack key for generating temporary urls | | | ||||
|  |  | |||
|  | @ -630,7 +630,7 @@ the `epic` property: | |||
| 
 | ||||
| **Note**: The `closed_by` attribute was [introduced in GitLab 10.6](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/17042). This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists. | ||||
| 
 | ||||
| **Note**: The `epic_iid` attribute is deprecated and [will be removed in 13.0](https://gitlab.com/gitlab-org/gitlab/issues/35157). | ||||
| **Note**: The `epic_iid` attribute is deprecated and [will be removed in version 5](https://gitlab.com/gitlab-org/gitlab/issues/35157). | ||||
| Please use `iid` of the `epic` attribute instead. | ||||
| 
 | ||||
| ## New issue | ||||
|  | @ -657,7 +657,7 @@ POST /projects/:id/issues | |||
| | `discussion_to_resolve`                   | string         | no       | The ID of a discussion to resolve. This will fill in the issue with a default description and mark the discussion as resolved. Use in combination with `merge_request_to_resolve_discussions_of`. | | ||||
| | `weight` **(STARTER)**                    | integer        | no       | The weight of the issue. Valid values are greater than or equal to 0. | | ||||
| | `epic_id` **(ULTIMATE)** | integer | no | ID of the epic to add the issue to. Valid values are greater than or equal to 0. | | ||||
| | `epic_iid` **(ULTIMATE)** | integer | no | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [will be removed in 13.0](https://gitlab.com/gitlab-org/gitlab/issues/35157)) | | ||||
| | `epic_iid` **(ULTIMATE)** | integer | no | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [will be removed in version 5](https://gitlab.com/gitlab-org/gitlab/issues/35157)) | | ||||
| 
 | ||||
| ```shell | ||||
| curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues?title=Issues%20with%20auth&labels=bug | ||||
|  | @ -773,7 +773,7 @@ PUT /projects/:id/issues/:issue_iid | |||
| | `weight` **(STARTER)** | integer | no | The weight of the issue. Valid values are greater than or equal to 0. 0                                                                    | | ||||
| | `discussion_locked` | boolean | no  | Flag indicating if the issue's discussion is locked. If the discussion is locked only project members can add or edit comments. | | ||||
| | `epic_id` **(ULTIMATE)** | integer | no | ID of the epic to add the issue to. Valid values are greater than or equal to 0. | | ||||
| | `epic_iid` **(ULTIMATE)** | integer | no | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [will be removed in 13.0](https://gitlab.com/gitlab-org/gitlab/issues/35157)) | | ||||
| | `epic_iid` **(ULTIMATE)** | integer | no | IID of the epic to add the issue to. Valid values are greater than or equal to 0. (deprecated, [will be removed in version 5](https://gitlab.com/gitlab-org/gitlab/issues/35157)) | | ||||
| 
 | ||||
| ```shell | ||||
| curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues/85?state_event=close | ||||
|  |  | |||
|  | @ -142,21 +142,31 @@ Starting with GitLab 12.0, Git is required to be compiled with `libpcre2`. | |||
| Find out if that's the case: | ||||
| 
 | ||||
| ```shell | ||||
| ldd /usr/local/bin/git | grep pcre2 | ||||
| ldd $(which git) | grep pcre2 | ||||
| ``` | ||||
| 
 | ||||
| The output should be similar to: | ||||
| The output should contain `libpcre2-8.so.0`. | ||||
| 
 | ||||
| ```plaintext | ||||
| libpcre2-8.so.0 => /usr/lib/libpcre2-8.so.0 (0x00007f08461c3000) | ||||
| ``` | ||||
| 
 | ||||
| Is the system packaged Git too old, or not compiled with pcre2? Remove it and compile from source: | ||||
| Is the system packaged Git too old, or not compiled with pcre2? | ||||
| Remove it: | ||||
| 
 | ||||
| ```shell | ||||
| # Remove packaged Git | ||||
| sudo apt-get remove git-core | ||||
| ``` | ||||
| 
 | ||||
| On Ubuntu, install Git from [its official PPA](https://git-scm.com/download/linux): | ||||
| 
 | ||||
| ```shell | ||||
| # run as root! | ||||
| add-apt-repository ppa:git-core/ppa | ||||
| apt update | ||||
| apt install git | ||||
| # repeat libpcre2 check as above | ||||
| ``` | ||||
| 
 | ||||
| On Debian, use the following compilation instructions: | ||||
| 
 | ||||
| ```shell | ||||
| # Install dependencies | ||||
| sudo apt-get install -y libcurl4-openssl-dev libexpat1-dev gettext libz-dev libssl-dev build-essential | ||||
| 
 | ||||
|  | @ -180,7 +190,7 @@ make prefix=/usr/local all | |||
| # Install into /usr/local/bin | ||||
| sudo make prefix=/usr/local install | ||||
| 
 | ||||
| # When editing config/gitlab.yml (Step 5), change the git -> bin_path to /usr/local/bin/git | ||||
| # When editing config/gitlab.yml later, change the git -> bin_path to /usr/local/bin/git | ||||
| ``` | ||||
| 
 | ||||
| For the [Custom Favicon](../user/admin_area/appearance.md#favicon) to work, GraphicsMagick | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ that are in common for all providers that we need to consider. | |||
|   automatically create an account. It defaults to `false`. If `false` users must | ||||
|   be created manually or they will not be able to sign in via OmniAuth. | ||||
| - `auto_link_ldap_user` can be used if you have [LDAP / ActiveDirectory](ldap.md) | ||||
|   integration enabled. It defaults to false. When enabled, users automatically | ||||
|   integration enabled. It defaults to `false`. When enabled, users automatically | ||||
|   created through an OmniAuth provider will have their LDAP identity created in GitLab as well. | ||||
| - `block_auto_created_users` defaults to `true`. If `true` auto created users will | ||||
|   be blocked by default and will have to be unblocked by an administrator before | ||||
|  |  | |||
|  | @ -112,7 +112,6 @@ are adding support for [PHP](https://gitlab.com/gitlab-org/gitlab/-/merge_reques | |||
| | [Opkg](https://gitlab.com/gitlab-org/gitlab/issues/36894) | Optimize your work with OpenWrt using Opkg repositories. | | ||||
| | [P2](https://gitlab.com/gitlab-org/gitlab/issues/36895) | Host all your Eclipse plugins in your own GitLab P2 repository. | | ||||
| | [Puppet](https://gitlab.com/gitlab-org/gitlab/issues/36897) | Configuration management meets repository management with Puppet repositories. | | ||||
| | [PyPi](https://gitlab.com/gitlab-org/gitlab/issues/10483) | Host PyPi distributions. | | ||||
| | [RPM](https://gitlab.com/gitlab-org/gitlab/issues/5932) | Distribute RPMs directly from GitLab. | | ||||
| | [RubyGems](https://gitlab.com/gitlab-org/gitlab/issues/803) | Use GitLab to host your own gems. | | ||||
| | [SBT](https://gitlab.com/gitlab-org/gitlab/issues/36898) | Resolve dependencies from and deploy build output to SBT repositories when running SBT builds. | | ||||
|  |  | |||
|  | @ -363,6 +363,14 @@ You do not need a token to run `npm install` unless your project is private (the | |||
| NPM_TOKEN=<your_token> npm install | ||||
| ``` | ||||
| 
 | ||||
| ### `npm install` returns `npm ERR! 403 Forbidden` | ||||
| 
 | ||||
| - Check that your token is not expired and has appropriate permissions. | ||||
| - Check if you have attempted to publish a package with a name that already exists within a given scope. | ||||
| - Ensure the scoped packages URL includes a trailing slash: | ||||
|   - Correct: `//gitlab.com/api/v4/packages/npm/` | ||||
|   - Incorrect: `//gitlab.com/api/v4/packages/npm` | ||||
| 
 | ||||
| ## NPM dependencies metadata | ||||
| 
 | ||||
| > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/11867) in GitLab Premium 12.6. | ||||
|  |  | |||
|  | @ -4,7 +4,8 @@ | |||
| 
 | ||||
| Working with multiple people on the same file can be a risk. Conflicts when merging a non-text file are hard to overcome and will require a lot of manual work to resolve. File Locking helps you avoid these merge conflicts and better manage your binary files. | ||||
| 
 | ||||
| With File Locaking, you can lock any file or directory, make your changes, and then unlock it so another member of the team can edit it. | ||||
| With File Locking, you can lock any file or directory, make your changes, and | ||||
| then unlock it so another member of the team can edit it. | ||||
| 
 | ||||
| ## Overview | ||||
| 
 | ||||
|  |  | |||
|  | @ -24,6 +24,11 @@ Changes are saved immediately. | |||
| 
 | ||||
|  | ||||
| 
 | ||||
| The last way to set a due date is by using [quick actions](../quick_actions.md), directly in an issue's description or comment: | ||||
| 
 | ||||
| - `/due <date>`: set due date. Examples of valid `<date>` include `in 2 days`, `this Friday`, and `December 31st`. | ||||
| - `/remove_due_date`: remove due date. | ||||
| 
 | ||||
| ## Making use of due dates | ||||
| 
 | ||||
| Issues that have a due date can be easily seen in the issue tracker, | ||||
|  |  | |||
|  | @ -22,7 +22,7 @@ module Gitlab | |||
|           project: project.path, | ||||
|           namespace: project.namespace.path, | ||||
|           return_url: return_url, | ||||
|           is_supported_content: supported_content? | ||||
|           is_supported_content: supported_content?.to_s | ||||
|         } | ||||
|       end | ||||
| 
 | ||||
|  |  | |||
|  | @ -2994,9 +2994,6 @@ msgstr "" | |||
| msgid "BambooService|You must set up automatic revision labeling and a repository trigger in Bamboo." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Batch operations" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "BatchComments|Delete all pending comments" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -9215,7 +9212,10 @@ msgstr "" | |||
| msgid "Filter by milestone name" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Filter by name..." | ||||
| msgid "Filter by name" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Filter by status" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Filter by two-factor authentication" | ||||
|  | @ -11208,6 +11208,9 @@ msgstr "" | |||
| msgid "In order to tailor your experience with GitLab we<br>would like to know a bit more about you." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "In progress" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "In the next step, you'll be able to select the projects you want to import." | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -17592,6 +17595,9 @@ msgstr "" | |||
| msgid "Resync" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Resync all" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Resync all %{replicableType}" | ||||
| msgstr "" | ||||
| 
 | ||||
|  |  | |||
|  | @ -40,7 +40,7 @@ | |||
|     "@babel/preset-env": "^7.8.4", | ||||
|     "@gitlab/at.js": "1.5.5", | ||||
|     "@gitlab/svgs": "1.121.0", | ||||
|     "@gitlab/ui": "12.3.0", | ||||
|     "@gitlab/ui": "13.5.0", | ||||
|     "@gitlab/visual-review-tools": "1.6.1", | ||||
|     "@sentry/browser": "^5.10.2", | ||||
|     "@sourcegraph/code-host-integration": "0.0.37", | ||||
|  |  | |||
|  | @ -10,6 +10,8 @@ describe('Autosave', () => { | |||
|   const field = $('<textarea></textarea>'); | ||||
|   const key = 'key'; | ||||
|   const fallbackKey = 'fallbackKey'; | ||||
|   const lockVersionKey = 'lockVersionKey'; | ||||
|   const lockVersion = 1; | ||||
| 
 | ||||
|   describe('class constructor', () => { | ||||
|     beforeEach(() => { | ||||
|  | @ -30,6 +32,13 @@ describe('Autosave', () => { | |||
|       expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled(); | ||||
|       expect(autosave.isLocalStorageAvailable).toBe(true); | ||||
|     }); | ||||
| 
 | ||||
|     it('should set .isLocalStorageAvailable if lockVersion is passed', () => { | ||||
|       autosave = new Autosave(field, key, null, lockVersion); | ||||
| 
 | ||||
|       expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled(); | ||||
|       expect(autosave.isLocalStorageAvailable).toBe(true); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('restore', () => { | ||||
|  | @ -96,6 +105,40 @@ describe('Autosave', () => { | |||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('getSavedLockVersion', () => { | ||||
|     beforeEach(() => { | ||||
|       autosave = { | ||||
|         field, | ||||
|         key, | ||||
|         lockVersionKey, | ||||
|       }; | ||||
|     }); | ||||
| 
 | ||||
|     describe('if .isLocalStorageAvailable is `false`', () => { | ||||
|       beforeEach(() => { | ||||
|         autosave.isLocalStorageAvailable = false; | ||||
| 
 | ||||
|         Autosave.prototype.getSavedLockVersion.call(autosave); | ||||
|       }); | ||||
| 
 | ||||
|       it('should not call .getItem', () => { | ||||
|         expect(window.localStorage.getItem).not.toHaveBeenCalled(); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     describe('if .isLocalStorageAvailable is `true`', () => { | ||||
|       beforeEach(() => { | ||||
|         autosave.isLocalStorageAvailable = true; | ||||
|       }); | ||||
| 
 | ||||
|       it('should call .getItem', () => { | ||||
|         Autosave.prototype.getSavedLockVersion.call(autosave); | ||||
| 
 | ||||
|         expect(window.localStorage.getItem).toHaveBeenCalledWith(lockVersionKey); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('save', () => { | ||||
|     beforeEach(() => { | ||||
|       autosave = { reset: jest.fn() }; | ||||
|  | @ -128,10 +171,51 @@ describe('Autosave', () => { | |||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('save with lockVersion', () => { | ||||
|     beforeEach(() => { | ||||
|       autosave = { | ||||
|         field, | ||||
|         key, | ||||
|         lockVersionKey, | ||||
|         lockVersion, | ||||
|         isLocalStorageAvailable: true, | ||||
|       }; | ||||
|     }); | ||||
| 
 | ||||
|     describe('lockVersion is valid', () => { | ||||
|       it('should call .setItem', () => { | ||||
|         Autosave.prototype.save.call(autosave); | ||||
|         expect(window.localStorage.setItem).toHaveBeenCalledWith(lockVersionKey, lockVersion); | ||||
|       }); | ||||
| 
 | ||||
|       it('should call .setItem when version is 0', () => { | ||||
|         autosave.lockVersion = 0; | ||||
|         Autosave.prototype.save.call(autosave); | ||||
|         expect(window.localStorage.setItem).toHaveBeenCalledWith( | ||||
|           lockVersionKey, | ||||
|           autosave.lockVersion, | ||||
|         ); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     describe('lockVersion is invalid', () => { | ||||
|       it('should not call .setItem with lockVersion', () => { | ||||
|         delete autosave.lockVersion; | ||||
|         Autosave.prototype.save.call(autosave); | ||||
| 
 | ||||
|         expect(window.localStorage.setItem).not.toHaveBeenCalledWith( | ||||
|           lockVersionKey, | ||||
|           autosave.lockVersion, | ||||
|         ); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   describe('reset', () => { | ||||
|     beforeEach(() => { | ||||
|       autosave = { | ||||
|         key, | ||||
|         lockVersionKey, | ||||
|       }; | ||||
|     }); | ||||
| 
 | ||||
|  | @ -156,6 +240,7 @@ describe('Autosave', () => { | |||
| 
 | ||||
|       it('should call .removeItem', () => { | ||||
|         expect(window.localStorage.removeItem).toHaveBeenCalledWith(key); | ||||
|         expect(window.localStorage.removeItem).toHaveBeenCalledWith(lockVersionKey); | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
|  | @ -166,8 +251,8 @@ describe('Autosave', () => { | |||
|         field, | ||||
|         key, | ||||
|         fallbackKey, | ||||
|         isLocalStorageAvailable: true, | ||||
|       }; | ||||
|       autosave.isLocalStorageAvailable = true; | ||||
|     }); | ||||
| 
 | ||||
|     it('should call .getItem', () => { | ||||
|  | @ -185,7 +270,8 @@ describe('Autosave', () => { | |||
|     it('should call .removeItem for key and fallbackKey', () => { | ||||
|       Autosave.prototype.reset.call(autosave); | ||||
| 
 | ||||
|       expect(window.localStorage.removeItem).toHaveBeenCalledTimes(2); | ||||
|       expect(window.localStorage.removeItem).toHaveBeenCalledWith(fallbackKey); | ||||
|       expect(window.localStorage.removeItem).toHaveBeenCalledWith(key); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  |  | |||
|  | @ -24,38 +24,38 @@ describe Gitlab::StaticSiteEditor::Config do | |||
|         project: 'project', | ||||
|         project_id: project.id, | ||||
|         return_url: 'http://example.com', | ||||
|         is_supported_content: true | ||||
|         is_supported_content: 'true' | ||||
|       ) | ||||
|     end | ||||
| 
 | ||||
|     context 'when branch is not master' do | ||||
|       let(:ref) { 'my-branch' } | ||||
| 
 | ||||
|       it { is_expected.to include(is_supported_content: false) } | ||||
|       it { is_expected.to include(is_supported_content: 'false') } | ||||
|     end | ||||
| 
 | ||||
|     context 'when file does not have a markdown extension' do | ||||
|       let(:file_path) { 'README.txt' } | ||||
| 
 | ||||
|       it { is_expected.to include(is_supported_content: false) } | ||||
|       it { is_expected.to include(is_supported_content: 'false') } | ||||
|     end | ||||
| 
 | ||||
|     context 'when file does not have an extension' do | ||||
|       let(:file_path) { 'README' } | ||||
| 
 | ||||
|       it { is_expected.to include(is_supported_content: false) } | ||||
|       it { is_expected.to include(is_supported_content: 'false') } | ||||
|     end | ||||
| 
 | ||||
|     context 'when file does not exist' do | ||||
|       let(:file_path) { 'UNKNOWN.md' } | ||||
| 
 | ||||
|       it { is_expected.to include(is_supported_content: false) } | ||||
|       it { is_expected.to include(is_supported_content: 'false') } | ||||
|     end | ||||
| 
 | ||||
|     context 'when repository is empty' do | ||||
|       let(:project) { create(:project_empty_repo) } | ||||
| 
 | ||||
|       it { is_expected.to include(is_supported_content: false) } | ||||
|       it { is_expected.to include(is_supported_content: 'false') } | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -78,4 +78,21 @@ describe ResourceMilestoneEvent, type: :model do | |||
|       let(:query_method) { :remove? } | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#milestone_title' do | ||||
|     let(:milestone) { create(:milestone, title: 'v2.3') } | ||||
|     let(:event) { create(:resource_milestone_event, milestone: milestone) } | ||||
| 
 | ||||
|     it 'returns the expected title' do | ||||
|       expect(event.milestone_title).to eq('v2.3') | ||||
|     end | ||||
| 
 | ||||
|     context 'when milestone is nil' do | ||||
|       let(:event) { create(:resource_milestone_event, milestone: nil) } | ||||
| 
 | ||||
|       it 'returns nil' do | ||||
|         expect(event.milestone_title).to be_nil | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -78,6 +78,14 @@ describe API::Terraform::State do | |||
| 
 | ||||
|           expect(response).to have_gitlab_http_status(:ok) | ||||
|         end | ||||
| 
 | ||||
|         context 'on Unicorn', :unicorn do | ||||
|           it 'updates the state' do | ||||
|             expect { request }.to change { Terraform::State.count }.by(0) | ||||
| 
 | ||||
|             expect(response).to have_gitlab_http_status(:ok) | ||||
|           end | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context 'without body' do | ||||
|  | @ -112,6 +120,14 @@ describe API::Terraform::State do | |||
| 
 | ||||
|           expect(response).to have_gitlab_http_status(:ok) | ||||
|         end | ||||
| 
 | ||||
|         context 'on Unicorn', :unicorn do | ||||
|           it 'creates a new state' do | ||||
|             expect { request }.to change { Terraform::State.count }.by(1) | ||||
| 
 | ||||
|             expect(response).to have_gitlab_http_status(:ok) | ||||
|           end | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context 'without body' do | ||||
|  |  | |||
|  | @ -89,7 +89,7 @@ describe Issuable::Clone::AttributesRewriter do | |||
| 
 | ||||
|         create_event(milestone1_project1) | ||||
|         create_event(milestone2_project1) | ||||
|         create_event(milestone1_project1, 'remove') | ||||
|         create_event(nil, 'remove') | ||||
|         create_event(milestone3_project1) | ||||
|       end | ||||
| 
 | ||||
|  | @ -101,7 +101,7 @@ describe Issuable::Clone::AttributesRewriter do | |||
| 
 | ||||
|         expect_milestone_event(new_issue_milestone_events.first, milestone: milestone1_project2, action: 'add', state: 'opened') | ||||
|         expect_milestone_event(new_issue_milestone_events.second, milestone: milestone2_project2, action: 'add', state: 'opened') | ||||
|         expect_milestone_event(new_issue_milestone_events.third, milestone: milestone1_project2, action: 'remove', state: 'opened') | ||||
|         expect_milestone_event(new_issue_milestone_events.third, milestone: nil, action: 'remove', state: 'opened') | ||||
|       end | ||||
| 
 | ||||
|       def create_event(milestone, action = 'add') | ||||
|  | @ -109,7 +109,7 @@ describe Issuable::Clone::AttributesRewriter do | |||
|       end | ||||
| 
 | ||||
|       def expect_milestone_event(event, expected_attrs) | ||||
|         expect(event.milestone_id).to eq(expected_attrs[:milestone].id) | ||||
|         expect(event.milestone_id).to eq(expected_attrs[:milestone]&.id) | ||||
|         expect(event.action).to eq(expected_attrs[:action]) | ||||
|         expect(event.state).to eq(expected_attrs[:state]) | ||||
|       end | ||||
|  |  | |||
|  | @ -0,0 +1,27 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| REQUEST_CLASSES = [ | ||||
|   ::Grape::Request, | ||||
|   ::Rack::Request | ||||
| ].freeze | ||||
| 
 | ||||
| def request_body_class | ||||
|   return ::Unicorn::TeeInput if defined?(::Unicorn) | ||||
| 
 | ||||
|   Class.new(StringIO) do | ||||
|     def string | ||||
|       raise NotImplementedError, '#string is only valid under Puma which uses StringIO, use #read instead' | ||||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| RSpec.configure do |config| | ||||
|   config.before(:each, :unicorn) do | ||||
|     REQUEST_CLASSES.each do |request_class| | ||||
|       allow_any_instance_of(request_class) | ||||
|         .to receive(:body).and_wrap_original do |m, *args| | ||||
|           request_body_class.new(m.call(*args).read) | ||||
|         end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -786,10 +786,10 @@ | |||
|   resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.121.0.tgz#77083a68f72e9aa0e294da7715f378eef13b839e" | ||||
|   integrity sha512-scz/6Y/eED7RMFLAlhT6PwXwe0Wj8ivnRsyulk9NXKoqUmAqZliNmBmzYsHy5bFf9NB6xVV/rOk1/92nbi/Yaw== | ||||
| 
 | ||||
| "@gitlab/ui@12.3.0": | ||||
|   version "12.3.0" | ||||
|   resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-12.3.0.tgz#9234205887675a6d13a51945ee62efc3c8b5e890" | ||||
|   integrity sha512-XrHC2pK7qlwy6K3OR/+iCP8TDewn3jaDIHCfHjt/KOwvD5LsEmam9RHjTiZ4epPZXLv4+JxCzbc4R+euEbIQ7g== | ||||
| "@gitlab/ui@13.5.0": | ||||
|   version "13.5.0" | ||||
|   resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-13.5.0.tgz#bb8d90baea80066e5360457386d8153724998043" | ||||
|   integrity sha512-f4k6zKcJWRNV5ho7SXz0gL4VU4n+ljB52VrUrfJ1WTrESGpIFlTU17/Ac4ZMYySZuUXzmLulf9BXEN5HWCetTQ== | ||||
|   dependencies: | ||||
|     "@babel/standalone" "^7.0.0" | ||||
|     "@gitlab/vue-toasted" "^1.3.0" | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue