diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-27 18:10:16 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-04-27 18:10:16 +0300 |
commit | c2908ec6a0d7b62996cdb8da0350705bdad691bf (patch) | |
tree | 1280356af695cfb7774b2aa9ea08631292795bb9 | |
parent | 45999bfdec535b959f46fa4ed8f761bb3eadfed4 (diff) |
Add latest changes from gitlab-org/gitlab@master
30 files changed, 320 insertions, 60 deletions
diff --git a/app/assets/javascripts/autosave.js b/app/assets/javascripts/autosave.js index 07d79ea1c70..5f50fcc112e 100644 --- a/app/assets/javascripts/autosave.js +++ b/app/assets/javascripts/autosave.js @@ -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); } diff --git a/app/assets/javascripts/snippets/components/snippet_blob_view.vue b/app/assets/javascripts/snippets/components/snippet_blob_view.vue index 02a0fc7686d..d615eaadb78 100644 --- a/app/assets/javascripts/snippets/components/snippet_blob_view.vue +++ b/app/assets/javascripts/snippets/components/snippet_blob_view.vue @@ -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" /> diff --git a/app/assets/javascripts/static_site_editor/index.js b/app/assets/javascripts/static_site_editor/index.js index 15d668fd431..fe5d11f1bd9 100644 --- a/app/assets/javascripts/static_site_editor/index.js +++ b/app/assets/javascripts/static_site_editor/index.js @@ -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, diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index 37f9bdc8fa4..69253b55188 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -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 diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index b8fe2a47b30..9cb345724cc 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -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 diff --git a/app/models/resource_milestone_event.rb b/app/models/resource_milestone_event.rb index a40af22061e..a0655c3a4ab 100644 --- a/app/models/resource_milestone_event.rb +++ b/app/models/resource_milestone_event.rb @@ -25,4 +25,8 @@ class ResourceMilestoneEvent < ResourceEvent def self.issuable_attrs %i(issue merge_request).freeze end + + def milestone_title + milestone&.title + end end diff --git a/app/services/issuable/clone/attributes_rewriter.rb b/app/services/issuable/clone/attributes_rewriter.rb index 55f5629baac..78d3fb2e4d2 100644 --- a/app/services/issuable/clone/attributes_rewriter.rb +++ b/app/services/issuable/clone/attributes_rewriter.rb @@ -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 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]) + if event.remove? + event_attributes_with_milestone(event, nil) + else + matching_destination_milestone = matching_milestone(event.milestone_title) + + 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 diff --git a/changelogs/unreleased/add-ops-strategies-user-lists-table.yml b/changelogs/unreleased/add-ops-strategies-user-lists-table.yml new file mode 100644 index 00000000000..245e4499206 --- /dev/null +++ b/changelogs/unreleased/add-ops-strategies-user-lists-table.yml @@ -0,0 +1,5 @@ +--- +title: Create operations_strategies_user_lists table +merge_request: 30243 +author: +type: added diff --git a/changelogs/unreleased/dmishunov-clone-btn-margin.yml b/changelogs/unreleased/dmishunov-clone-btn-margin.yml new file mode 100644 index 00000000000..197d5974ac0 --- /dev/null +++ b/changelogs/unreleased/dmishunov-clone-btn-margin.yml @@ -0,0 +1,5 @@ +--- +title: Added right margin to Clone Snippet button +merge_request: 30471 +author: +type: fixed diff --git a/db/migrate/20200422213749_create_operations_strategies_user_lists.rb b/db/migrate/20200422213749_create_operations_strategies_user_lists.rb new file mode 100644 index 00000000000..113f2f2f54a --- /dev/null +++ b/db/migrate/20200422213749_create_operations_strategies_user_lists.rb @@ -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 diff --git a/db/structure.sql b/db/structure.sql index 0b0f66835c1..fca564dde8c 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -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 diff --git a/doc/administration/geo/replication/updating_the_geo_nodes.md b/doc/administration/geo/replication/updating_the_geo_nodes.md index 3ce35798406..fa1576e19eb 100644 --- a/doc/administration/geo/replication/updating_the_geo_nodes.md +++ b/doc/administration/geo/replication/updating_the_geo_nodes.md @@ -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) diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md index 294c776980d..5aff9f9c358 100644 --- a/doc/administration/uploads.md +++ b/doc/administration/uploads.md @@ -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 | | diff --git a/doc/api/issues.md b/doc/api/issues.md index 14f81d7d327..02cfe38fae2 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -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 diff --git a/doc/install/installation.md b/doc/install/installation.md index 3652b31c1aa..27616f6c1ff 100644 --- a/doc/install/installation.md +++ b/doc/install/installation.md @@ -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: + +```shell +sudo apt-get remove git-core ``` -Is the system packaged Git too old, or not compiled with pcre2? Remove it and compile from source: +On Ubuntu, install Git from [its official PPA](https://git-scm.com/download/linux): ```shell -# Remove packaged Git -sudo apt-get remove git-core +# 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 diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index 5634ad95cf7..2afdeccb764 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -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 diff --git a/doc/user/packages/index.md b/doc/user/packages/index.md index 8e98dd70346..cb3cb26ebb1 100644 --- a/doc/user/packages/index.md +++ b/doc/user/packages/index.md @@ -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. | diff --git a/doc/user/packages/npm_registry/index.md b/doc/user/packages/npm_registry/index.md index e66b3d1ac63..5187201ec60 100644 --- a/doc/user/packages/npm_registry/index.md +++ b/doc/user/packages/npm_registry/index.md @@ -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. diff --git a/doc/user/project/file_lock.md b/doc/user/project/file_lock.md index b5350515c30..9069a231db4 100644 --- a/doc/user/project/file_lock.md +++ b/doc/user/project/file_lock.md @@ -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 diff --git a/doc/user/project/issues/due_dates.md b/doc/user/project/issues/due_dates.md index f70597f6875..0be0cdd11bd 100644 --- a/doc/user/project/issues/due_dates.md +++ b/doc/user/project/issues/due_dates.md @@ -24,6 +24,11 @@ Changes are saved immediately. ![Edit a due date via the sidebar](img/due_dates_edit_sidebar.png) +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, diff --git a/lib/gitlab/static_site_editor/config.rb b/lib/gitlab/static_site_editor/config.rb index 41d54ee0a92..1862d949817 100644 --- a/lib/gitlab/static_site_editor/config.rb +++ b/lib/gitlab/static_site_editor/config.rb @@ -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 diff --git a/locale/gitlab.pot b/locale/gitlab.pot index b12731340e5..87736edd036 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -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 "" diff --git a/package.json b/package.json index ec77c17a82d..5b630d0c5f4 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/spec/frontend/autosave_spec.js b/spec/frontend/autosave_spec.js index 3119477f385..bbdf3c6f91d 100644 --- a/spec/frontend/autosave_spec.js +++ b/spec/frontend/autosave_spec.js @@ -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); }); }); }); diff --git a/spec/lib/gitlab/static_site_editor/config_spec.rb b/spec/lib/gitlab/static_site_editor/config_spec.rb index 8f61476722d..b32af912ad9 100644 --- a/spec/lib/gitlab/static_site_editor/config_spec.rb +++ b/spec/lib/gitlab/static_site_editor/config_spec.rb @@ -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 diff --git a/spec/models/resource_milestone_event_spec.rb b/spec/models/resource_milestone_event_spec.rb index bf8672f95c9..3f8d8b4c1df 100644 --- a/spec/models/resource_milestone_event_spec.rb +++ b/spec/models/resource_milestone_event_spec.rb @@ -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 diff --git a/spec/requests/api/terraform/state_spec.rb b/spec/requests/api/terraform/state_spec.rb index 88c277f4e08..844cd948411 100644 --- a/spec/requests/api/terraform/state_spec.rb +++ b/spec/requests/api/terraform/state_spec.rb @@ -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 diff --git a/spec/services/issuable/clone/attributes_rewriter_spec.rb b/spec/services/issuable/clone/attributes_rewriter_spec.rb index 9111b19d7b7..8cb37917239 100644 --- a/spec/services/issuable/clone/attributes_rewriter_spec.rb +++ b/spec/services/issuable/clone/attributes_rewriter_spec.rb @@ -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 diff --git a/spec/support/unicorn.rb b/spec/support/unicorn.rb new file mode 100644 index 00000000000..0b01fc9e26c --- /dev/null +++ b/spec/support/unicorn.rb @@ -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 diff --git a/yarn.lock b/yarn.lock index a0298450219..5c2e6274a32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -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" |