diff options
-rw-r--r-- | .rubocop_todo/style/inline_disable_annotation.yml | 1 | ||||
-rw-r--r-- | Gemfile | 2 | ||||
-rw-r--r-- | Gemfile.checksum | 7 | ||||
-rw-r--r-- | Gemfile.lock | 16 | ||||
-rw-r--r-- | app/models/analytics/cycle_analytics/aggregation.rb | 19 | ||||
-rw-r--r-- | app/models/concerns/analytics/cycle_analytics/parentable.rb | 11 | ||||
-rw-r--r-- | app/models/resource_label_event.rb | 6 | ||||
-rw-r--r-- | db/post_migrate/20231225070431_async_validate_finding_id_on_vulnerabilities.rb | 17 | ||||
-rw-r--r-- | db/schema_migrations/20231225070431 | 1 | ||||
-rw-r--r-- | doc/development/feature_development.md | 1 | ||||
-rw-r--r-- | doc/development/remote_development/index.md | 16 | ||||
-rw-r--r-- | doc/user/workspace/index.md | 1 | ||||
-rw-r--r-- | locale/gitlab.pot | 3 | ||||
-rw-r--r-- | spec/models/analytics/cycle_analytics/aggregation_spec.rb | 57 | ||||
-rw-r--r-- | spec/models/resource_label_event_spec.rb | 62 | ||||
-rw-r--r-- | spec/support/helpers/cycle_analytics_helpers.rb | 4 | ||||
-rw-r--r-- | spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb | 13 |
17 files changed, 171 insertions, 66 deletions
diff --git a/.rubocop_todo/style/inline_disable_annotation.yml b/.rubocop_todo/style/inline_disable_annotation.yml index 1a6226514e6..db42f855d1a 100644 --- a/.rubocop_todo/style/inline_disable_annotation.yml +++ b/.rubocop_todo/style/inline_disable_annotation.yml @@ -390,7 +390,6 @@ Style/InlineDisableAnnotation: - 'app/models/commit.rb' - 'app/models/commit_collection.rb' - 'app/models/commit_status.rb' - - 'app/models/concerns/analytics/cycle_analytics/parentable.rb' - 'app/models/concerns/approvable.rb' - 'app/models/concerns/async_devise_email.rb' - 'app/models/concerns/atomic_internal_id.rb' @@ -288,7 +288,7 @@ gem 'redis-namespace', '~> 1.10.0' # rubocop:todo Gemfile/MissingFeatureCategory gem 'connection_pool', '~> 2.4' # rubocop:todo Gemfile/MissingFeatureCategory # Redis session store -gem 'redis-actionpack', '~> 5.3.0' # rubocop:todo Gemfile/MissingFeatureCategory +gem 'redis-actionpack', '~> 5.4.0' # rubocop:todo Gemfile/MissingFeatureCategory # Discord integration gem 'discordrb-webhooks', '~> 3.4', require: false, feature_category: :integrations diff --git a/Gemfile.checksum b/Gemfile.checksum index e545531d857..73998da5c88 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -482,6 +482,7 @@ {"name":"rack-oauth2","version":"1.21.3","platform":"ruby","checksum":"4e72a79dd6a866692e84422a552b27c38a5a1918ded06661e04910f2bbe676ba"}, {"name":"rack-protection","version":"2.2.2","platform":"ruby","checksum":"fd41414dbabbec274af0bdb1f72a48504449de4d979782c9af38cbb5dfff3299"}, {"name":"rack-proxy","version":"0.7.7","platform":"ruby","checksum":"446a4b57001022145d5c3ba73b775f66a2260eaf7420c6907483141900395c8a"}, +{"name":"rack-session","version":"1.0.2","platform":"ruby","checksum":"a02115e5420b4de036839b9811e3f7967d73446a554b42aa45106af335851d76"}, {"name":"rack-test","version":"2.1.0","platform":"ruby","checksum":"0c61fc61904049d691922ea4bb99e28004ed3f43aa5cfd495024cc345f125dfb"}, {"name":"rack-timeout","version":"0.6.3","platform":"ruby","checksum":"1754892eacc124d405e7f1145731ec9b7421ebd1bee5d51ddc18b72c204d0ab3"}, {"name":"rails","version":"7.0.8","platform":"ruby","checksum":"8e43af921acf766fb429126f020ec90c3b25809631f8fbdff95c3553828d5867"}, @@ -512,10 +513,10 @@ {"name":"recursive-open-struct","version":"1.1.3","platform":"ruby","checksum":"a3538a72552fcebcd0ada657bdff313641a4a5fbc482c08cfb9a65acb1c9de5a"}, {"name":"redcarpet","version":"3.6.0","platform":"ruby","checksum":"8ad1889c0355ff4c47174af14edd06d62f45a326da1da6e8a121d59bdcd2e9e9"}, {"name":"redis","version":"4.8.0","platform":"ruby","checksum":"2000cf5014669c9dc821704b6d322a35a9a33852a95208911d9175d63b448a44"}, -{"name":"redis-actionpack","version":"5.3.0","platform":"ruby","checksum":"3fb1ad0a8fd9d26a289c9399bb609dcaef38bf37711e6f677a53ca728fc19140"}, +{"name":"redis-actionpack","version":"5.4.0","platform":"ruby","checksum":"f10cf649ab05914716d63334d7f709221ecc883b87cf348f90ecfe0c35ea3540"}, {"name":"redis-namespace","version":"1.10.0","platform":"ruby","checksum":"2c1c6ea7c6c5e343e75b9bee3aa4c265e364a5b9966507397467af2bb3758d94"}, -{"name":"redis-rack","version":"2.1.4","platform":"ruby","checksum":"0872eecb303e483c3863d6bd0d47323d230640d41c1a4ac4a2c7596ec0b1774c"}, -{"name":"redis-store","version":"1.9.1","platform":"ruby","checksum":"7b4c7438d46f7b7ce8f67fc0eda3a04fc67d32d28cf606cc98a5df4d2b77071d"}, +{"name":"redis-rack","version":"3.0.0","platform":"ruby","checksum":"abb50b82ae10ad4d11ca2e4901bfc2b98256cdafbbd95f80c86fc9e001478380"}, +{"name":"redis-store","version":"1.10.0","platform":"ruby","checksum":"f258894f9f7e82834308a3d86242294f0cff2c9db9ae66e5cb4c553a5ec8b09e"}, {"name":"regexp_parser","version":"2.6.0","platform":"ruby","checksum":"f163ba463a45ca2f2730e0902f2475bb0eefcd536dfc2f900a86d1e5a7d7a556"}, {"name":"regexp_property_values","version":"1.0.0","platform":"java","checksum":"5e26782b01241616855c4ee7bb8a62fce9387e484f2d3eaf04f2a0633708222e"}, {"name":"regexp_property_values","version":"1.0.0","platform":"ruby","checksum":"162499dc0bba1e66d334273a059f207a61981cc8cc69d2ca743594e7886d080f"}, diff --git a/Gemfile.lock b/Gemfile.lock index 77c866875b2..c543ecffc6c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1297,6 +1297,8 @@ GEM rack rack-proxy (0.7.7) rack + rack-session (1.0.2) + rack (< 3) rack-test (2.1.0) rack (>= 1.3) rack-timeout (0.6.3) @@ -1352,17 +1354,17 @@ GEM recursive-open-struct (1.1.3) redcarpet (3.6.0) redis (4.8.0) - redis-actionpack (5.3.0) + redis-actionpack (5.4.0) actionpack (>= 5, < 8) - redis-rack (>= 2.1.0, < 3) + redis-rack (>= 2.1.0, < 4) redis-store (>= 1.1.0, < 2) redis-namespace (1.10.0) redis (>= 4) - redis-rack (2.1.4) - rack (>= 2.0.8, < 3) + redis-rack (3.0.0) + rack-session (>= 0.2.0) redis-store (>= 1.2, < 2) - redis-store (1.9.1) - redis (>= 4, < 5) + redis-store (1.10.0) + redis (>= 4, < 6) regexp_parser (2.6.0) regexp_property_values (1.0.0) representable (3.2.0) @@ -2035,7 +2037,7 @@ DEPENDENCIES re2 (= 2.5.0) recaptcha (~> 5.12) redis (~> 4.8.0) - redis-actionpack (~> 5.3.0) + redis-actionpack (~> 5.4.0) redis-namespace (~> 1.10.0) request_store (~> 1.5.1) responders (~> 3.0) diff --git a/app/models/analytics/cycle_analytics/aggregation.rb b/app/models/analytics/cycle_analytics/aggregation.rb index 0f8e184933e..5ac5437a442 100644 --- a/app/models/analytics/cycle_analytics/aggregation.rb +++ b/app/models/analytics/cycle_analytics/aggregation.rb @@ -59,19 +59,26 @@ class Analytics::CycleAnalytics::Aggregation < ApplicationRecord estimation < 1 ? nil : estimation.from_now end - def self.safe_create_for_namespace(group_or_project_namespace) + def self.safe_create_for_namespace(target_namespace) # Namespaces::ProjectNamespace has no root_ancestor # Related: https://gitlab.com/gitlab-org/gitlab/-/issues/386124 - group = group_or_project_namespace.is_a?(Group) ? group_or_project_namespace : group_or_project_namespace.parent - top_level_group = group.root_ancestor - aggregation = find_by(group_id: top_level_group.id) + namespace = if target_namespace.is_a?(Group) || target_namespace.is_a?(Namespaces::UserNamespace) + target_namespace + else + target_namespace.parent + end + # personal namespace projects and associated ProjectNamespace respond to `namespace` + # and this is close enough to "root ancestor" + top_level_namespace = + target_namespace.respond_to?(:root_ancestor) ? namespace.root_ancestor : namespace.namespace + aggregation = find_by(group_id: top_level_namespace.id) return aggregation if aggregation&.enabled? # At this point we're sure that the group is licensed, we can always enable the aggregation. # This re-enables the aggregation in case the group downgraded and later upgraded the license. - upsert({ group_id: top_level_group.id, enabled: true }) + upsert({ group_id: top_level_namespace.id, enabled: true }) - find(top_level_group.id) + find(top_level_namespace.id) end private diff --git a/app/models/concerns/analytics/cycle_analytics/parentable.rb b/app/models/concerns/analytics/cycle_analytics/parentable.rb index 785f6eea6bf..90a38e3c58c 100644 --- a/app/models/concerns/analytics/cycle_analytics/parentable.rb +++ b/app/models/concerns/analytics/cycle_analytics/parentable.rb @@ -6,16 +6,7 @@ module Analytics extend ActiveSupport::Concern included do - belongs_to :namespace, class_name: 'Namespace', foreign_key: :group_id, optional: false # rubocop: disable Rails/InverseOf - - validate :ensure_namespace_type - - def ensure_namespace_type - return if namespace.nil? - return if namespace.is_a?(::Namespaces::ProjectNamespace) || namespace.is_a?(::Group) - - errors.add(:namespace, s_('CycleAnalytics|the assigned object is not supported')) - end + belongs_to :namespace, class_name: 'Namespace', foreign_key: :group_id, optional: false # rubocop: disable Rails/InverseOf -- this relation is not present on Namespace end end end diff --git a/app/models/resource_label_event.rb b/app/models/resource_label_event.rb index ad1ce740c89..e912e57f39e 100644 --- a/app/models/resource_label_event.rb +++ b/app/models/resource_label_event.rb @@ -45,7 +45,7 @@ class ResourceLabelEvent < ResourceEvent end def group - issuable.group if issuable.respond_to?(:group) + issuable.resource_parent if issuable.resource_parent.is_a?(Group) end def outdated_markdown? @@ -93,7 +93,9 @@ class ResourceLabelEvent < ResourceEvent end def label_url_method - issuable.is_a?(MergeRequest) ? :project_merge_requests_url : :project_issues_url + return :project_merge_requests_url if issuable.is_a?(MergeRequest) + + issuable.project_id.nil? ? :group_work_items_url : :project_issues_url end def broadcast_notes_changed diff --git a/db/post_migrate/20231225070431_async_validate_finding_id_on_vulnerabilities.rb b/db/post_migrate/20231225070431_async_validate_finding_id_on_vulnerabilities.rb new file mode 100644 index 00000000000..dfab0d6eab0 --- /dev/null +++ b/db/post_migrate/20231225070431_async_validate_finding_id_on_vulnerabilities.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AsyncValidateFindingIdOnVulnerabilities < Gitlab::Database::Migration[2.2] + # obtained by running `\d vulnerabilities` on https://console.postgres.ai + FK_NAME = :fk_4e64972902 + + milestone '16.8' + + # TODO: FK to be validated synchronously in https://gitlab.com/gitlab-org/gitlab/-/issues/425409 + def up + prepare_async_foreign_key_validation :vulnerabilities, :finding_id, name: FK_NAME + end + + def down + unprepare_async_foreign_key_validation :vulnerabilities, :finding_id, name: FK_NAME + end +end diff --git a/db/schema_migrations/20231225070431 b/db/schema_migrations/20231225070431 new file mode 100644 index 00000000000..7cbc466473b --- /dev/null +++ b/db/schema_migrations/20231225070431 @@ -0,0 +1 @@ +1298d9dd3c6b909a3129b5375477b466b7970347cf65d0654bf3076f87658fd0
\ No newline at end of file diff --git a/doc/development/feature_development.md b/doc/development/feature_development.md index a5a6439b420..59785554ce6 100644 --- a/doc/development/feature_development.md +++ b/doc/development/feature_development.md @@ -97,6 +97,7 @@ Consult these topics for information on contributing to specific GitLab features - [Application limits](application_limits.md) - [AI features](ai_features/index.md) - [Application settings](application_settings.md) +- [Remote Development](remote_development/index.md) ### Import and Export diff --git a/doc/development/remote_development/index.md b/doc/development/remote_development/index.md new file mode 100644 index 00000000000..a4026e21315 --- /dev/null +++ b/doc/development/remote_development/index.md @@ -0,0 +1,16 @@ +--- +stage: Create +group: Web IDE +info: Any user with at least the Maintainer role can merge updates to this content. For details, see https://docs.gitlab.com/ee/development/development_processes.html#development-guidelines-review. +--- + +# Remote Development + +[Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/105783) in GitLab 16.0. + +## Workspaces Feature Developer Documentation + +Currently, the majority of the developer documentation for the [Remote Development Workspaces feature](../../user/workspace/index.md) +is located in the separate [`gitlab-remote-development-docs` project](https://gitlab.com/gitlab-org/remote-development/gitlab-remote-development-docs/-/blob/main/README.md). + +Parts of that documentation will eventually be migrated here. diff --git a/doc/user/workspace/index.md b/doc/user/workspace/index.md index d24102afcf9..309c5d19a51 100644 --- a/doc/user/workspace/index.md +++ b/doc/user/workspace/index.md @@ -181,3 +181,4 @@ For more information, see the ## Related topics - [GitLab workspaces demo](https://go.gitlab.com/qtu66q) +- [Developer documentation](../../development/remote_development/index.md)] diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 3c34d2ff287..f3ae0a1e487 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -15294,9 +15294,6 @@ msgstr "" msgid "CycleAnalytics|project dropdown filter" msgstr "" -msgid "CycleAnalytics|the assigned object is not supported" -msgstr "" - msgid "DAG visualization requires at least 3 dependent jobs." msgstr "" diff --git a/spec/models/analytics/cycle_analytics/aggregation_spec.rb b/spec/models/analytics/cycle_analytics/aggregation_spec.rb index e69093f454a..5494df84d68 100644 --- a/spec/models/analytics/cycle_analytics/aggregation_spec.rb +++ b/spec/models/analytics/cycle_analytics/aggregation_spec.rb @@ -131,39 +131,52 @@ RSpec.describe Analytics::CycleAnalytics::Aggregation, type: :model, feature_cat end describe '#safe_create_for_namespace' do - let_it_be(:group) { create(:group) } - let_it_be(:subgroup) { create(:group, parent: group) } + context 'when group namespace is provided' do + let_it_be(:group) { create(:group) } + let_it_be(:subgroup) { create(:group, parent: group) } - it 'creates the aggregation record' do - record = described_class.safe_create_for_namespace(group) - - expect(record).to be_persisted - end - - context 'when non top-level group is given' do - it 'creates the aggregation record for the top-level group' do - record = described_class.safe_create_for_namespace(subgroup) + it 'creates the aggregation record' do + record = described_class.safe_create_for_namespace(group) expect(record).to be_persisted end - end - context 'when the record is already present' do - it 'does nothing' do - described_class.safe_create_for_namespace(group) + context 'when non top-level group is given' do + it 'creates the aggregation record for the top-level group' do + record = described_class.safe_create_for_namespace(subgroup) + + expect(record).to be_persisted + end + end - expect do + context 'when the record is already present' do + it 'does nothing' do described_class.safe_create_for_namespace(group) - described_class.safe_create_for_namespace(subgroup) - end.not_to change { described_class.count } + + expect do + described_class.safe_create_for_namespace(group) + described_class.safe_create_for_namespace(subgroup) + end.not_to change { described_class.count } + end + end + + context 'when the aggregation was disabled for some reason' do + it 're-enables the aggregation' do + create(:cycle_analytics_aggregation, enabled: false, namespace: group) + + aggregation = described_class.safe_create_for_namespace(group) + + expect(aggregation).to be_enabled + end end end - context 'when the aggregation was disabled for some reason' do - it 're-enables the aggregation' do - create(:cycle_analytics_aggregation, enabled: false, namespace: group) + context 'when personal namespace is provided' do + let_it_be(:user2) { create(:user) } + let_it_be(:project) { create(:project, :public, namespace: user2.namespace) } - aggregation = described_class.safe_create_for_namespace(group) + it 'is successful' do + aggregation = described_class.safe_create_for_namespace(user2.namespace) expect(aggregation).to be_enabled end diff --git a/spec/models/resource_label_event_spec.rb b/spec/models/resource_label_event_spec.rb index 8cc89578e0e..27a39672994 100644 --- a/spec/models/resource_label_event_spec.rb +++ b/spec/models/resource_label_event_spec.rb @@ -3,12 +3,13 @@ require 'spec_helper' RSpec.describe ResourceLabelEvent, feature_category: :team_planning, type: :model do - let_it_be(:project) { create(:project, :repository) } + let_it_be(:group) { create(:group) } + let_it_be(:project) { create(:project, :repository, group: group) } let_it_be(:issue) { create(:issue, project: project) } let_it_be(:merge_request) { create(:merge_request, source_project: project) } let_it_be(:label) { create(:label, project: project) } - subject { build(:resource_label_event, issue: issue, label: label) } + subject(:resource_label_event) { build(:resource_label_event, issue: issue, label: label) } it_behaves_like 'having unique enum values' @@ -95,6 +96,63 @@ RSpec.describe ResourceLabelEvent, feature_category: :team_planning, type: :mode end end + describe '#reference_html' do + subject { Nokogiri::HTML.fragment(label_event.reference_html).css('a').first.attr('href') } + + before do + label_event.refresh_invalid_reference + end + + context 'when resource event belongs to a group level issue' do + let(:group_label) { create(:group_label, group: group) } + let(:label_event) do + group_issue = create(:issue, :group_level, namespace: group) + + create(:resource_label_event, issue: group_issue, label: group_label) + end + + it { is_expected.to eq(Gitlab::Routing.url_helpers.group_work_items_path(group, label_name: group_label.title)) } + end + + context 'when resource event belongs to a project level issue' do + let(:label_event) { resource_label_event } + + it { is_expected.to eq(Gitlab::Routing.url_helpers.project_issues_path(project, label_name: label.title)) } + end + + context 'when resource event belongs to a merge request' do + let(:label_event) { create(:resource_label_event, merge_request: merge_request, label: label) } + + it do + is_expected.to eq(Gitlab::Routing.url_helpers.project_merge_requests_path(project, label_name: label.title)) + end + end + end + + describe '#group' do + subject { build_stubbed(:resource_label_event, **issuable_attributes).group } + + context 'when issuable is a merge request' do + let(:issuable_attributes) { { merge_request: merge_request } } + + it { is_expected.to be_nil } + end + + context 'when issuable is an issue' do + context 'when issue exists at the project level' do + let(:issuable_attributes) { { issue: issue } } + + it { is_expected.to be_nil } + end + + context 'when issue exists at the group level' do + let(:issuable_attributes) { { issue: build_stubbed(:issue, :group_level, namespace: group) } } + + it { is_expected.to eq(group) } + end + end + end + describe '.visible_to_user?' do let_it_be(:user) { create(:user) } let_it_be(:issue_project) { create(:project) } diff --git a/spec/support/helpers/cycle_analytics_helpers.rb b/spec/support/helpers/cycle_analytics_helpers.rb index e4683cbf3c0..3f04d984e55 100644 --- a/spec/support/helpers/cycle_analytics_helpers.rb +++ b/spec/support/helpers/cycle_analytics_helpers.rb @@ -94,8 +94,8 @@ module CycleAnalyticsHelpers wait_for_requests end - def create_value_stream_aggregation(group_or_project_namespace) - aggregation = Analytics::CycleAnalytics::Aggregation.safe_create_for_namespace(group_or_project_namespace) + def create_value_stream_aggregation(namespace) + aggregation = Analytics::CycleAnalytics::Aggregation.safe_create_for_namespace(namespace) Analytics::CycleAnalytics::AggregatorService.new(aggregation: aggregation).execute end diff --git a/spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb b/spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb index 5fd0e685c67..a6f028a5a04 100644 --- a/spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb +++ b/spec/support/shared_examples/analytics/cycle_analytics/parentable_examples.rb @@ -14,15 +14,14 @@ RSpec.shared_examples 'value stream analytics namespace models' do end end - context 'when Namespace is given' do - it 'fails' do - namespace = create(:namespace) + context 'when personal namespace is given' do + it 'is valid' do + namespace = create(:namespace, owner: create(:user)) model = build(factory_name, namespace: namespace) - expect(model).to be_invalid - - error_message = s_('CycleAnalytics|the assigned object is not supported') - expect(model.errors.messages_for(:namespace)).to eq([error_message]) + expect(model).to be_valid + expect(model.save).to be(true) + expect(model.namespace).to eq(namespace) end end end |