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'; | import AccessorUtilities from './lib/utils/accessor'; | ||||||
| 
 | 
 | ||||||
| export default class Autosave { | export default class Autosave { | ||||||
|   constructor(field, key, fallbackKey) { |   constructor(field, key, fallbackKey, lockVersion) { | ||||||
|     this.field = field; |     this.field = field; | ||||||
| 
 | 
 | ||||||
|     this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); |     this.isLocalStorageAvailable = AccessorUtilities.isLocalStorageAccessSafe(); | ||||||
|  | @ -12,6 +12,8 @@ export default class Autosave { | ||||||
|     } |     } | ||||||
|     this.key = `autosave/${key}`; |     this.key = `autosave/${key}`; | ||||||
|     this.fallbackKey = fallbackKey; |     this.fallbackKey = fallbackKey; | ||||||
|  |     this.lockVersionKey = `${this.key}/lockVersion`; | ||||||
|  |     this.lockVersion = lockVersion; | ||||||
|     this.field.data('autosave', this); |     this.field.data('autosave', this); | ||||||
|     this.restore(); |     this.restore(); | ||||||
|     this.field.on('input', () => this.save()); |     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() { |   save() { | ||||||
|     if (!this.field.length) return; |     if (!this.field.length) return; | ||||||
| 
 | 
 | ||||||
|  | @ -49,6 +56,9 @@ export default class Autosave { | ||||||
|       if (this.fallbackKey) { |       if (this.fallbackKey) { | ||||||
|         window.localStorage.setItem(this.fallbackKey, text); |         window.localStorage.setItem(this.fallbackKey, text); | ||||||
|       } |       } | ||||||
|  |       if (this.lockVersion !== undefined) { | ||||||
|  |         window.localStorage.setItem(this.lockVersionKey, this.lockVersion); | ||||||
|  |       } | ||||||
|       return window.localStorage.setItem(this.key, text); |       return window.localStorage.setItem(this.key, text); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | @ -58,6 +68,7 @@ export default class Autosave { | ||||||
|   reset() { |   reset() { | ||||||
|     if (!this.isLocalStorageAvailable) return; |     if (!this.isLocalStorageAvailable) return; | ||||||
| 
 | 
 | ||||||
|  |     window.localStorage.removeItem(this.lockVersionKey); | ||||||
|     window.localStorage.removeItem(this.fallbackKey); |     window.localStorage.removeItem(this.fallbackKey); | ||||||
|     return window.localStorage.removeItem(this.key); |     return window.localStorage.removeItem(this.key); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  | @ -75,6 +75,7 @@ export default { | ||||||
|         <template #actions> |         <template #actions> | ||||||
|           <clone-dropdown-button |           <clone-dropdown-button | ||||||
|             v-if="canBeCloned" |             v-if="canBeCloned" | ||||||
|  |             class="mr-2" | ||||||
|             :ssh-link="snippet.sshUrlToRepo" |             :ssh-link="snippet.sshUrlToRepo" | ||||||
|             :http-link="snippet.httpUrlToRepo" |             :http-link="snippet.httpUrlToRepo" | ||||||
|           /> |           /> | ||||||
|  |  | ||||||
|  | @ -1,14 +1,14 @@ | ||||||
| import Vue from 'vue'; | import Vue from 'vue'; | ||||||
|  | import { parseBoolean } from '~/lib/utils/common_utils'; | ||||||
| import StaticSiteEditor from './components/static_site_editor.vue'; | import StaticSiteEditor from './components/static_site_editor.vue'; | ||||||
| import createStore from './store'; | import createStore from './store'; | ||||||
| 
 | 
 | ||||||
| const initStaticSiteEditor = el => { | const initStaticSiteEditor = el => { | ||||||
|   const { projectId, path: sourcePath, returnUrl } = el.dataset; |   const { isSupportedContent, projectId, path: sourcePath, returnUrl } = el.dataset; | ||||||
|   const isSupportedContent = 'isSupportedContent' in el.dataset; |  | ||||||
| 
 | 
 | ||||||
|   const store = createStore({ |   const store = createStore({ | ||||||
|     initialState: { |     initialState: { | ||||||
|       isSupportedContent, |       isSupportedContent: parseBoolean(isSupportedContent), | ||||||
|       projectId, |       projectId, | ||||||
|       returnUrl, |       returnUrl, | ||||||
|       sourcePath, |       sourcePath, | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ class Projects::RefsController < Projects::ApplicationController | ||||||
|   before_action :authorize_download_code! |   before_action :authorize_download_code! | ||||||
| 
 | 
 | ||||||
|   before_action only: [:logs_tree] do |   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 |   end | ||||||
| 
 | 
 | ||||||
|   def switch |   def switch | ||||||
|  |  | ||||||
|  | @ -16,7 +16,7 @@ class Projects::TreeController < Projects::ApplicationController | ||||||
|   before_action :authorize_edit_tree!, only: [:create_dir] |   before_action :authorize_edit_tree!, only: [:create_dir] | ||||||
| 
 | 
 | ||||||
|   before_action only: [:show] do |   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 |   end | ||||||
| 
 | 
 | ||||||
|   def show |   def show | ||||||
|  |  | ||||||
|  | @ -25,4 +25,8 @@ class ResourceMilestoneEvent < ResourceEvent | ||||||
|   def self.issuable_attrs |   def self.issuable_attrs | ||||||
|     %i(issue merge_request).freeze |     %i(issue merge_request).freeze | ||||||
|   end |   end | ||||||
|  | 
 | ||||||
|  |   def milestone_title | ||||||
|  |     milestone&.title | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -67,22 +67,30 @@ module Issuable | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       def copy_resource_milestone_events |       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| |         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_with_milestone(event, matching_destination_milestone) 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]) |  | ||||||
|           end |           end | ||||||
|         end |         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) |       def copy_events(table_name, events_to_copy) | ||||||
|         events_to_copy.find_in_batches do |batch| |         events_to_copy.find_in_batches do |batch| | ||||||
|           events = batch.map do |event| |           events = batch.map do |event| | ||||||
|  | @ -96,6 +104,11 @@ module Issuable | ||||||
|       def entity_key |       def entity_key | ||||||
|         new_entity.class.name.parameterize('_').foreign_key |         new_entity.class.name.parameterize('_').foreign_key | ||||||
|       end |       end | ||||||
|  | 
 | ||||||
|  |       def milestone_events_supported? | ||||||
|  |         original_entity.respond_to?(:resource_milestone_events) && | ||||||
|  |           new_entity.respond_to?(:resource_milestone_events) | ||||||
|  |       end | ||||||
|     end |     end | ||||||
|   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; | 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 ( | CREATE TABLE public.operations_user_lists ( | ||||||
|     id bigint NOT NULL, |     id bigint NOT NULL, | ||||||
|     project_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 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.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); | 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 | ALTER TABLE ONLY public.operations_strategies | ||||||
|     ADD CONSTRAINT operations_strategies_pkey PRIMARY KEY (id); |     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 | ALTER TABLE ONLY public.operations_user_lists | ||||||
|     ADD CONSTRAINT operations_user_lists_pkey PRIMARY KEY (id); |     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_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_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_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 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); | 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 | ALTER TABLE ONLY public.clusters_applications_fluentd | ||||||
|     ADD CONSTRAINT fk_rails_4319b1dcd2 FOREIGN KEY (cluster_id) REFERENCES public.clusters(id) ON DELETE CASCADE; |     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 | ALTER TABLE ONLY public.lfs_file_locks | ||||||
|     ADD CONSTRAINT fk_rails_43df7a0412 FOREIGN KEY (project_id) REFERENCES public.projects(id) ON DELETE CASCADE; |     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 | 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; |     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 | ALTER TABLE ONLY public.issue_tracker_data | ||||||
|     ADD CONSTRAINT fk_rails_ccc0840427 FOREIGN KEY (service_id) REFERENCES public.services(id) ON DELETE CASCADE; |     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 | 20200420172927 | ||||||
| 20200420201933 | 20200420201933 | ||||||
| 20200421233150 | 20200421233150 | ||||||
|  | 20200422213749 | ||||||
| 20200423075720 | 20200423075720 | ||||||
| 20200423080334 | 20200423080334 | ||||||
| 20200423080607 | 20200423080607 | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ Updating Geo nodes involves performing: | ||||||
| Depending on which version of Geo you are updating to/from, there may be | Depending on which version of Geo you are updating to/from, there may be | ||||||
| different steps. | 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.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.2](version_specific_updates.md#updating-to-gitlab-122) | ||||||
| - [Updating to GitLab 12.1](version_specific_updates.md#updating-to-gitlab-121) | - [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 | | | 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_access_key_id` | AWS credentials, or compatible | | | ||||||
| | `aws_secret_access_key` | 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 | | | `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 | | | `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 | | | `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) | | | `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 | | | `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 | | `use_iam_profile` | Set to `true` to use IAM profile instead of access keys | false | ||||||
| 
 | 
 | ||||||
| **In Omnibus installations:** | **In Omnibus installations:** | ||||||
| 
 | 
 | ||||||
|  | @ -149,8 +149,8 @@ Note that Oracle Cloud S3 must be sure to use the following settings: | ||||||
| 
 | 
 | ||||||
| | Setting | Value | | | Setting | Value | | ||||||
| |---------|-------| | |---------|-------| | ||||||
| | `enable_signature_v4_streaming` | false | | | `enable_signature_v4_streaming` | `false` | | ||||||
| | `path_style` | true | | | `path_style` | `true` | | ||||||
| 
 | 
 | ||||||
| If `enable_signature_v4_streaming` is set to `true`, you may see the | If `enable_signature_v4_streaming` is set to `true`, you may see the | ||||||
| following error: | following error: | ||||||
|  | @ -165,7 +165,7 @@ The connection settings match those provided by [Fog](https://github.com/fog), a | ||||||
| 
 | 
 | ||||||
| | Setting | Description | Default | | | Setting | Description | Default | | ||||||
| |---------|-------------|---------| | |---------|-------------|---------| | ||||||
| | `provider` | Always `OpenStack` for compatible hosts | OpenStack | | | `provider` | Always `OpenStack` for compatible hosts | `OpenStack` | | ||||||
| | `openstack_username` | OpenStack username | | | | `openstack_username` | OpenStack username | | | ||||||
| | `openstack_api_key` | OpenStack API key  | | | | `openstack_api_key` | OpenStack API key  | | | ||||||
| | `openstack_temp_url_key` | OpenStack key for generating temporary urls | | | | `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 `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. | Please use `iid` of the `epic` attribute instead. | ||||||
| 
 | 
 | ||||||
| ## New issue | ## 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`. | | | `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. | | | `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_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 | ```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 | 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                                                                    | | | `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. | | | `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_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 | ```shell | ||||||
| curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/4/issues/85?state_event=close | 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: | Find out if that's the case: | ||||||
| 
 | 
 | ||||||
| ```shell | ```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 | Is the system packaged Git too old, or not compiled with pcre2? | ||||||
| libpcre2-8.so.0 => /usr/lib/libpcre2-8.so.0 (0x00007f08461c3000) | Remove it: | ||||||
| ``` |  | ||||||
| 
 |  | ||||||
| Is the system packaged Git too old, or not compiled with pcre2? Remove it and compile from source: |  | ||||||
| 
 | 
 | ||||||
| ```shell | ```shell | ||||||
| # Remove packaged Git |  | ||||||
| sudo apt-get remove git-core | 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 | # Install dependencies | ||||||
| sudo apt-get install -y libcurl4-openssl-dev libexpat1-dev gettext libz-dev libssl-dev build-essential | 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 | # Install into /usr/local/bin | ||||||
| sudo make prefix=/usr/local install | 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 | 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 |   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. |   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) | - `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. |   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 | - `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 |   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. | | | [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. | | | [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. | | | [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. | | | [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. | | | [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. | | | [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_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 | ## NPM dependencies metadata | ||||||
| 
 | 
 | ||||||
| > [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/11867) in GitLab Premium 12.6. | > [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. | 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 | ## 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 | ## Making use of due dates | ||||||
| 
 | 
 | ||||||
| Issues that have a due date can be easily seen in the issue tracker, | Issues that have a due date can be easily seen in the issue tracker, | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ module Gitlab | ||||||
|           project: project.path, |           project: project.path, | ||||||
|           namespace: project.namespace.path, |           namespace: project.namespace.path, | ||||||
|           return_url: return_url, |           return_url: return_url, | ||||||
|           is_supported_content: supported_content? |           is_supported_content: supported_content?.to_s | ||||||
|         } |         } | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -2994,9 +2994,6 @@ msgstr "" | ||||||
| msgid "BambooService|You must set up automatic revision labeling and a repository trigger in Bamboo." | msgid "BambooService|You must set up automatic revision labeling and a repository trigger in Bamboo." | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
| msgid "Batch operations" |  | ||||||
| msgstr "" |  | ||||||
| 
 |  | ||||||
| msgid "BatchComments|Delete all pending comments" | msgid "BatchComments|Delete all pending comments" | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
|  | @ -9215,7 +9212,10 @@ msgstr "" | ||||||
| msgid "Filter by milestone name" | msgid "Filter by milestone name" | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
| msgid "Filter by name..." | msgid "Filter by name" | ||||||
|  | msgstr "" | ||||||
|  | 
 | ||||||
|  | msgid "Filter by status" | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
| msgid "Filter by two-factor authentication" | 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." | msgid "In order to tailor your experience with GitLab we<br>would like to know a bit more about you." | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
|  | msgid "In progress" | ||||||
|  | msgstr "" | ||||||
|  | 
 | ||||||
| msgid "In the next step, you'll be able to select the projects you want to import." | msgid "In the next step, you'll be able to select the projects you want to import." | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
|  | @ -17592,6 +17595,9 @@ msgstr "" | ||||||
| msgid "Resync" | msgid "Resync" | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
|  | msgid "Resync all" | ||||||
|  | msgstr "" | ||||||
|  | 
 | ||||||
| msgid "Resync all %{replicableType}" | msgid "Resync all %{replicableType}" | ||||||
| msgstr "" | msgstr "" | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -40,7 +40,7 @@ | ||||||
|     "@babel/preset-env": "^7.8.4", |     "@babel/preset-env": "^7.8.4", | ||||||
|     "@gitlab/at.js": "1.5.5", |     "@gitlab/at.js": "1.5.5", | ||||||
|     "@gitlab/svgs": "1.121.0", |     "@gitlab/svgs": "1.121.0", | ||||||
|     "@gitlab/ui": "12.3.0", |     "@gitlab/ui": "13.5.0", | ||||||
|     "@gitlab/visual-review-tools": "1.6.1", |     "@gitlab/visual-review-tools": "1.6.1", | ||||||
|     "@sentry/browser": "^5.10.2", |     "@sentry/browser": "^5.10.2", | ||||||
|     "@sourcegraph/code-host-integration": "0.0.37", |     "@sourcegraph/code-host-integration": "0.0.37", | ||||||
|  |  | ||||||
|  | @ -10,6 +10,8 @@ describe('Autosave', () => { | ||||||
|   const field = $('<textarea></textarea>'); |   const field = $('<textarea></textarea>'); | ||||||
|   const key = 'key'; |   const key = 'key'; | ||||||
|   const fallbackKey = 'fallbackKey'; |   const fallbackKey = 'fallbackKey'; | ||||||
|  |   const lockVersionKey = 'lockVersionKey'; | ||||||
|  |   const lockVersion = 1; | ||||||
| 
 | 
 | ||||||
|   describe('class constructor', () => { |   describe('class constructor', () => { | ||||||
|     beforeEach(() => { |     beforeEach(() => { | ||||||
|  | @ -30,6 +32,13 @@ describe('Autosave', () => { | ||||||
|       expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled(); |       expect(AccessorUtilities.isLocalStorageAccessSafe).toHaveBeenCalled(); | ||||||
|       expect(autosave.isLocalStorageAvailable).toBe(true); |       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', () => { |   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', () => { |   describe('save', () => { | ||||||
|     beforeEach(() => { |     beforeEach(() => { | ||||||
|       autosave = { reset: jest.fn() }; |       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', () => { |   describe('reset', () => { | ||||||
|     beforeEach(() => { |     beforeEach(() => { | ||||||
|       autosave = { |       autosave = { | ||||||
|         key, |         key, | ||||||
|  |         lockVersionKey, | ||||||
|       }; |       }; | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|  | @ -156,6 +240,7 @@ describe('Autosave', () => { | ||||||
| 
 | 
 | ||||||
|       it('should call .removeItem', () => { |       it('should call .removeItem', () => { | ||||||
|         expect(window.localStorage.removeItem).toHaveBeenCalledWith(key); |         expect(window.localStorage.removeItem).toHaveBeenCalledWith(key); | ||||||
|  |         expect(window.localStorage.removeItem).toHaveBeenCalledWith(lockVersionKey); | ||||||
|       }); |       }); | ||||||
|     }); |     }); | ||||||
|   }); |   }); | ||||||
|  | @ -166,8 +251,8 @@ describe('Autosave', () => { | ||||||
|         field, |         field, | ||||||
|         key, |         key, | ||||||
|         fallbackKey, |         fallbackKey, | ||||||
|  |         isLocalStorageAvailable: true, | ||||||
|       }; |       }; | ||||||
|       autosave.isLocalStorageAvailable = true; |  | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     it('should call .getItem', () => { |     it('should call .getItem', () => { | ||||||
|  | @ -185,7 +270,8 @@ describe('Autosave', () => { | ||||||
|     it('should call .removeItem for key and fallbackKey', () => { |     it('should call .removeItem for key and fallbackKey', () => { | ||||||
|       Autosave.prototype.reset.call(autosave); |       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: 'project', | ||||||
|         project_id: project.id, |         project_id: project.id, | ||||||
|         return_url: 'http://example.com', |         return_url: 'http://example.com', | ||||||
|         is_supported_content: true |         is_supported_content: 'true' | ||||||
|       ) |       ) | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context 'when branch is not master' do |     context 'when branch is not master' do | ||||||
|       let(:ref) { 'my-branch' } |       let(:ref) { 'my-branch' } | ||||||
| 
 | 
 | ||||||
|       it { is_expected.to include(is_supported_content: false) } |       it { is_expected.to include(is_supported_content: 'false') } | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context 'when file does not have a markdown extension' do |     context 'when file does not have a markdown extension' do | ||||||
|       let(:file_path) { 'README.txt' } |       let(:file_path) { 'README.txt' } | ||||||
| 
 | 
 | ||||||
|       it { is_expected.to include(is_supported_content: false) } |       it { is_expected.to include(is_supported_content: 'false') } | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context 'when file does not have an extension' do |     context 'when file does not have an extension' do | ||||||
|       let(:file_path) { 'README' } |       let(:file_path) { 'README' } | ||||||
| 
 | 
 | ||||||
|       it { is_expected.to include(is_supported_content: false) } |       it { is_expected.to include(is_supported_content: 'false') } | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context 'when file does not exist' do |     context 'when file does not exist' do | ||||||
|       let(:file_path) { 'UNKNOWN.md' } |       let(:file_path) { 'UNKNOWN.md' } | ||||||
| 
 | 
 | ||||||
|       it { is_expected.to include(is_supported_content: false) } |       it { is_expected.to include(is_supported_content: 'false') } | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     context 'when repository is empty' do |     context 'when repository is empty' do | ||||||
|       let(:project) { create(:project_empty_repo) } |       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 |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -78,4 +78,21 @@ describe ResourceMilestoneEvent, type: :model do | ||||||
|       let(:query_method) { :remove? } |       let(:query_method) { :remove? } | ||||||
|     end |     end | ||||||
|   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 | end | ||||||
|  |  | ||||||
|  | @ -78,6 +78,14 @@ describe API::Terraform::State do | ||||||
| 
 | 
 | ||||||
|           expect(response).to have_gitlab_http_status(:ok) |           expect(response).to have_gitlab_http_status(:ok) | ||||||
|         end |         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 |       end | ||||||
| 
 | 
 | ||||||
|       context 'without body' do |       context 'without body' do | ||||||
|  | @ -112,6 +120,14 @@ describe API::Terraform::State do | ||||||
| 
 | 
 | ||||||
|           expect(response).to have_gitlab_http_status(:ok) |           expect(response).to have_gitlab_http_status(:ok) | ||||||
|         end |         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 |       end | ||||||
| 
 | 
 | ||||||
|       context 'without body' do |       context 'without body' do | ||||||
|  |  | ||||||
|  | @ -89,7 +89,7 @@ describe Issuable::Clone::AttributesRewriter do | ||||||
| 
 | 
 | ||||||
|         create_event(milestone1_project1) |         create_event(milestone1_project1) | ||||||
|         create_event(milestone2_project1) |         create_event(milestone2_project1) | ||||||
|         create_event(milestone1_project1, 'remove') |         create_event(nil, 'remove') | ||||||
|         create_event(milestone3_project1) |         create_event(milestone3_project1) | ||||||
|       end |       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.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.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 |       end | ||||||
| 
 | 
 | ||||||
|       def create_event(milestone, action = 'add') |       def create_event(milestone, action = 'add') | ||||||
|  | @ -109,7 +109,7 @@ describe Issuable::Clone::AttributesRewriter do | ||||||
|       end |       end | ||||||
| 
 | 
 | ||||||
|       def expect_milestone_event(event, expected_attrs) |       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.action).to eq(expected_attrs[:action]) | ||||||
|         expect(event.state).to eq(expected_attrs[:state]) |         expect(event.state).to eq(expected_attrs[:state]) | ||||||
|       end |       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" |   resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.121.0.tgz#77083a68f72e9aa0e294da7715f378eef13b839e" | ||||||
|   integrity sha512-scz/6Y/eED7RMFLAlhT6PwXwe0Wj8ivnRsyulk9NXKoqUmAqZliNmBmzYsHy5bFf9NB6xVV/rOk1/92nbi/Yaw== |   integrity sha512-scz/6Y/eED7RMFLAlhT6PwXwe0Wj8ivnRsyulk9NXKoqUmAqZliNmBmzYsHy5bFf9NB6xVV/rOk1/92nbi/Yaw== | ||||||
| 
 | 
 | ||||||
| "@gitlab/ui@12.3.0": | "@gitlab/ui@13.5.0": | ||||||
|   version "12.3.0" |   version "13.5.0" | ||||||
|   resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-12.3.0.tgz#9234205887675a6d13a51945ee62efc3c8b5e890" |   resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-13.5.0.tgz#bb8d90baea80066e5360457386d8153724998043" | ||||||
|   integrity sha512-XrHC2pK7qlwy6K3OR/+iCP8TDewn3jaDIHCfHjt/KOwvD5LsEmam9RHjTiZ4epPZXLv4+JxCzbc4R+euEbIQ7g== |   integrity sha512-f4k6zKcJWRNV5ho7SXz0gL4VU4n+ljB52VrUrfJ1WTrESGpIFlTU17/Ac4ZMYySZuUXzmLulf9BXEN5HWCetTQ== | ||||||
|   dependencies: |   dependencies: | ||||||
|     "@babel/standalone" "^7.0.0" |     "@babel/standalone" "^7.0.0" | ||||||
|     "@gitlab/vue-toasted" "^1.3.0" |     "@gitlab/vue-toasted" "^1.3.0" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue