diff --git a/app/assets/javascripts/organizations/users/components/user_details_drawer.vue b/app/assets/javascripts/organizations/users/components/user_details_drawer.vue
index 6aa5218db18..e84a72403e6 100644
--- a/app/assets/javascripts/organizations/users/components/user_details_drawer.vue
+++ b/app/assets/javascripts/organizations/users/components/user_details_drawer.vue
@@ -1,5 +1,5 @@
-
+
{ s_('The authentication token.') },
+ non_empty_password_title: -> { s_('ProjectService|Enter new token') },
+ non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
+ placeholder: '',
+ required: true
+
+ field :subdomain,
+ description: -> { s_('The subdomain setting.') },
+ exposes_secrets: true,
+ placeholder: ''
+ end
+
+ def execute(data)
+ return unless supported_events.include?(data[:object_kind])
+
+ url = "https://atlas.assembla.com/spaces/#{URI.encode_www_form_component(subdomain)}/github_tool?secret_key=#{URI.encode_www_form_component(token)}"
+ body = { payload: data }
+
+ Gitlab::HTTP.post(
+ url,
+ body: Gitlab::Json.dump(body),
+ headers: { 'Content-Type' => 'application/json' }
+ )
+ end
+ end
+ end
+end
diff --git a/app/models/integrations/assembla.rb b/app/models/integrations/assembla.rb
index c441ead50de..c2e56ae4066 100644
--- a/app/models/integrations/assembla.rb
+++ b/app/models/integrations/assembla.rb
@@ -2,44 +2,6 @@
module Integrations
class Assembla < Integration
- validates :token, presence: true, if: :activated?
-
- field :token,
- type: :password,
- description: -> { s_('The authentication token.') },
- non_empty_password_title: -> { s_('ProjectService|Enter new token') },
- non_empty_password_help: -> { s_('ProjectService|Leave blank to use your current token.') },
- placeholder: '',
- required: true
-
- field :subdomain,
- description: -> { s_('The subdomain setting.') },
- exposes_secrets: true,
- placeholder: ''
-
- def self.title
- 'Assembla'
- end
-
- def self.description
- _('Manage projects.')
- end
-
- def self.to_param
- 'assembla'
- end
-
- def self.supported_events
- %w[push]
- end
-
- def execute(data)
- return unless supported_events.include?(data[:object_kind])
-
- url = "https://atlas.assembla.com/spaces/#{subdomain}/github_tool?secret_key=#{token}"
- body = { payload: data }
-
- Gitlab::HTTP.post(url, body: Gitlab::Json.dump(body), headers: { 'Content-Type' => 'application/json' })
- end
+ include Integrations::Base::Assembla
end
end
diff --git a/app/models/integrations/instance/assembla.rb b/app/models/integrations/instance/assembla.rb
new file mode 100644
index 00000000000..c0ec37b60d6
--- /dev/null
+++ b/app/models/integrations/instance/assembla.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+module Integrations
+ module Instance
+ class Assembla < Integration
+ include Integrations::Base::Assembla
+ end
+ end
+end
diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb
index 74b904fad2f..a77ffd8ff6e 100644
--- a/app/models/network/graph.rb
+++ b/app/models/network/graph.rb
@@ -27,14 +27,8 @@ module Network
# https://gitlab.com/gitlab-org/gitlab-foss/issues/58013
Gitlab::GitalyClient.allow_n_plus_1_calls do
# Decorate with app/model/network/commit.rb
- if Feature.enabled?(:use_list_commits_rpc_network_graph, @project, type: :gitlab_com_derisk)
- list_commits(count_to_display_commit_in_center).map do |commit|
- Network::Commit.new(commit)
- end
- else
- find_commits(count_to_display_commit_in_center).map do |commit|
- Network::Commit.new(commit)
- end
+ list_commits(count_to_display_commit_in_center).map do |commit|
+ Network::Commit.new(commit)
end
end
end
@@ -76,11 +70,7 @@ module Network
offset = -1
skip = 0
while offset == -1
- tmp_commits = if Feature.enabled?(:use_list_commits_rpc_network_graph, @project, type: :gitlab_com_derisk)
- list_commits(skip)
- else
- find_commits(skip)
- end
+ tmp_commits = list_commits(skip)
if tmp_commits.present?
index = tmp_commits.index do |c|
diff --git a/app/models/personal_access_token.rb b/app/models/personal_access_token.rb
index 6a4e9c80700..73451e38b9b 100644
--- a/app/models/personal_access_token.rb
+++ b/app/models/personal_access_token.rb
@@ -49,6 +49,8 @@ class PersonalAccessToken < ApplicationRecord
scope :expiring_and_not_notified, ->(date) { where(["revoked = false AND expire_notification_delivered = false AND seven_days_notification_sent_at IS NULL AND expires_at >= CURRENT_DATE AND expires_at <= ?", date]) }
scope :expired_today_and_not_notified, -> { where(["revoked = false AND expires_at = CURRENT_DATE AND after_expiry_notification_delivered = false"]) }
scope :expired_before, ->(date) { expired.where(arel_table[:expires_at].lt(date)) }
+ scope :expires_before, ->(date) { where(arel_table[:expires_at].lteq(date)) }
+ scope :expires_after, ->(date) { where(arel_table[:expires_at].gteq(date)) }
scope :inactive, -> { where("revoked = true OR expires_at < CURRENT_DATE") }
scope :last_used_before_or_unused, ->(date) { where("personal_access_tokens.created_at < :date AND (last_used_at < :date OR last_used_at IS NULL)", date: date) }
scope :with_impersonation, -> { where(impersonation: true) }
diff --git a/app/models/project.rb b/app/models/project.rb
index a13823ab281..d025da9672d 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -817,6 +817,11 @@ class Project < ApplicationRecord
.where(project_pages_metadata: { project_id: nil })
end
+ scope :with_namespace_domain_pages, -> do
+ joins(:project_setting)
+ .where(project_setting: { pages_unique_domain_enabled: false })
+ end
+
scope :with_api_commit_entity_associations, -> {
preload(:project_feature, :route, namespace: [:route, :owner])
}
@@ -2358,6 +2363,10 @@ class Project < ApplicationRecord
!(pages_metadatum&.onboarding_complete || pages_deployed?)
end
+ def pages_unique_domain_enabled?
+ project_setting.pages_unique_domain_enabled
+ end
+
def remove_private_deploy_keys
exclude_keys_linked_to_other_projects = <<-SQL
NOT EXISTS (
@@ -3353,7 +3362,7 @@ class Project < ApplicationRecord
end
def pages_domain_present?(domain_url)
- pages_url == domain_url || pages_domains.exists?(domain: domain_url)
+ pages_url == domain_url || pages_domains.any? { |domain| domain.url == domain_url }
end
# overridden in EE
diff --git a/app/views/projects/usage_quotas/index.html.haml b/app/views/projects/usage_quotas/index.html.haml
index a549e5890d3..cd55c2a78d9 100644
--- a/app/views/projects/usage_quotas/index.html.haml
+++ b/app/views/projects/usage_quotas/index.html.haml
@@ -16,5 +16,6 @@
#js-storage-usage-app{ data: { project_path: @project.full_path } }
= render_if_exists 'projects/usage_quotas/transfer_tab_content'
= render_if_exists 'shared/usage_quotas/tabs_content/observability'
+ = render_if_exists 'projects/usage_quotas/pages'
= render 'shared/usage_quotas/index'
diff --git a/config/feature_flags/gitlab_com_derisk/use_list_commits_rpc_network_graph.yml b/config/feature_flags/gitlab_com_derisk/use_list_commits_rpc_network_graph.yml
deleted file mode 100644
index 0fe3f9c9379..00000000000
--- a/config/feature_flags/gitlab_com_derisk/use_list_commits_rpc_network_graph.yml
+++ /dev/null
@@ -1,9 +0,0 @@
----
-name: use_list_commits_rpc_network_graph
-feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/386449
-introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/164081
-rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/484681
-milestone: '17.4'
-group: group::source code
-type: gitlab_com_derisk
-default_enabled: false
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 21cb2813905..7706b52597a 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -1287,8 +1287,8 @@ production: &base
# private_key_file: /home/git/gitlab/config/topology-service-key.pem
cell:
- # id: 1
- # name: cell-1
+ # id: null
+ # name: null
# skip_sequence_alteration: false
gitlab_kas:
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 33511e71aac..6995e801ba4 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -1054,8 +1054,8 @@ Settings.topology_service['private_key_file'] ||= '/home/git/gitlab/config/topol
# Cells
#
Settings['cell'] ||= {}
-Settings.cell['id'] ||= 1
-Settings.cell['name'] ||= "cell-#{Settings.cell['id']}"
+Settings.cell['id'] ||= nil
+Settings.cell['name'] ||= nil
Settings.cell['skip_sequence_alteration'] ||= false
#
diff --git a/config/initializers/validate_cell_config.rb b/config/initializers/validate_cell_config.rb
new file mode 100644
index 00000000000..e949cf6f429
--- /dev/null
+++ b/config/initializers/validate_cell_config.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+return if Gitlab::Utils.to_boolean(ENV['SKIP_CELL_CONFIG_VALIDATION'], default: false)
+
+ValidationError = Class.new(StandardError)
+
+if Gitlab.config.cell.id.present? && !Gitlab.config.topology_service.enabled
+ raise ValidationError, "Topology Service is not configured, but Cell ID is set"
+end
+
+if Gitlab.config.topology_service.enabled && Gitlab.config.cell.id.blank?
+ raise ValidationError, "Topology Service is enabled, but Cell ID is not set"
+end
diff --git a/config/settings.rb b/config/settings.rb
index 8ba43f3fd07..f4ba6441448 100644
--- a/config/settings.rb
+++ b/config/settings.rb
@@ -179,16 +179,13 @@ Settings = GitlabSettings.load(file, Rails.env) do
[[Gitlab::SidekiqConfig::WorkerMatcher::WILDCARD_MATCH, 'default']]
end
+ # This method dictates whether the GitLab instance is part of a cells cluster
def topology_service_enabled?
topology_service && topology_service.respond_to?(:enabled) && topology_service.enabled
end
- def has_configured_cell?
- cell && cell.respond_to?(:name) && cell.name.present?
- end
-
def skip_sequence_alteration?
- has_configured_cell? && cell.respond_to?(:skip_sequence_alteration) && cell.skip_sequence_alteration
+ cell.respond_to?(:skip_sequence_alteration) && cell.skip_sequence_alteration
end
private
diff --git a/db/docs/instance_integrations.yml b/db/docs/instance_integrations.yml
index 6f0611627ab..7ed98afeb72 100644
--- a/db/docs/instance_integrations.yml
+++ b/db/docs/instance_integrations.yml
@@ -2,6 +2,7 @@
table_name: instance_integrations
classes:
- Integrations::Instance::Integration
+- Integrations::Instance::Assembla
feature_categories:
- integrations
description: Support 3rd party instance-wide integrations
diff --git a/db/migrate/20241118061025_update_workspaces_devfile_path_nullable.rb b/db/migrate/20241118061025_update_workspaces_devfile_path_nullable.rb
new file mode 100644
index 00000000000..1aa34861e08
--- /dev/null
+++ b/db/migrate/20241118061025_update_workspaces_devfile_path_nullable.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class UpdateWorkspacesDevfilePathNullable < Gitlab::Database::Migration[2.2]
+ milestone '17.8'
+ disable_ddl_transaction!
+
+ def up
+ change_column_null :workspaces, :devfile_path, true
+ end
+
+ def down
+ change_column_null :workspaces, :devfile_path, false
+ end
+end
diff --git a/db/migrate/20241118061030_rename_workspaces_devfile_ref_to_project_ref.rb b/db/migrate/20241118061030_rename_workspaces_devfile_ref_to_project_ref.rb
new file mode 100644
index 00000000000..d8d8eea0253
--- /dev/null
+++ b/db/migrate/20241118061030_rename_workspaces_devfile_ref_to_project_ref.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class RenameWorkspacesDevfileRefToProjectRef < Gitlab::Database::Migration[2.2]
+ milestone '17.8'
+ disable_ddl_transaction!
+
+ def up
+ rename_column_concurrently :workspaces, :devfile_ref, :project_ref
+ end
+
+ def down
+ undo_rename_column_concurrently :workspaces, :devfile_ref, :project_ref
+ end
+end
diff --git a/db/migrate/20250108131741_add_workflow_definition_to_workflows.rb b/db/migrate/20250108131741_add_workflow_definition_to_workflows.rb
new file mode 100644
index 00000000000..0b559fa64c5
--- /dev/null
+++ b/db/migrate/20250108131741_add_workflow_definition_to_workflows.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+# See https://docs.gitlab.com/ee/development/migration_style_guide.html
+# for more information on how to write migrations for GitLab.
+
+class AddWorkflowDefinitionToWorkflows < Gitlab::Database::Migration[2.2]
+ disable_ddl_transaction!
+
+ milestone '17.8'
+
+ def up
+ with_lock_retries do
+ add_column :duo_workflows_workflows, :workflow_definition, :text, default: 'software_development',
+ null: false
+ end
+
+ add_text_limit :duo_workflows_workflows, :workflow_definition, 255
+ end
+
+ def down
+ with_lock_retries do
+ remove_column :duo_workflows_workflows, :workflow_definition
+ end
+ end
+end
diff --git a/db/post_migrate/20241118061130_cleanup_workspaces_devfile_ref_rename.rb b/db/post_migrate/20241118061130_cleanup_workspaces_devfile_ref_rename.rb
new file mode 100644
index 00000000000..217f723f4bc
--- /dev/null
+++ b/db/post_migrate/20241118061130_cleanup_workspaces_devfile_ref_rename.rb
@@ -0,0 +1,14 @@
+# frozen_string_literal: true
+
+class CleanupWorkspacesDevfileRefRename < Gitlab::Database::Migration[2.2]
+ milestone '17.8'
+ disable_ddl_transaction!
+
+ def up
+ cleanup_concurrent_column_rename :workspaces, :devfile_ref, :project_ref
+ end
+
+ def down
+ undo_cleanup_concurrent_column_rename :workspaces, :devfile_ref, :project_ref
+ end
+end
diff --git a/db/schema_migrations/20241118061025 b/db/schema_migrations/20241118061025
new file mode 100644
index 00000000000..742a591c8ec
--- /dev/null
+++ b/db/schema_migrations/20241118061025
@@ -0,0 +1 @@
+2ab125f0a48dbece1a6e98b698f2f44734f07976e34909a502f7cfbb937d9c55
\ No newline at end of file
diff --git a/db/schema_migrations/20241118061030 b/db/schema_migrations/20241118061030
new file mode 100644
index 00000000000..2dba8d5e77a
--- /dev/null
+++ b/db/schema_migrations/20241118061030
@@ -0,0 +1 @@
+3de77a23770fa6eb0bfff996189e1f3b9748299fed8d01c0929423eba4570978
\ No newline at end of file
diff --git a/db/schema_migrations/20241118061130 b/db/schema_migrations/20241118061130
new file mode 100644
index 00000000000..a0981989ede
--- /dev/null
+++ b/db/schema_migrations/20241118061130
@@ -0,0 +1 @@
+32e2325b1442e77a35af0544cdc9a6ea7fab37d4f9185264923dcdc574da758e
\ No newline at end of file
diff --git a/db/schema_migrations/20250108131741 b/db/schema_migrations/20250108131741
new file mode 100644
index 00000000000..2520c46755d
--- /dev/null
+++ b/db/schema_migrations/20250108131741
@@ -0,0 +1 @@
+d037f2d4a30977b304f4df8da3374043d5e1fd15ddb7b066cf168c848148d825
\ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 6f74f6d35fd..3b552c5225f 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -12142,7 +12142,9 @@ CREATE TABLE duo_workflows_workflows (
status smallint DEFAULT 0 NOT NULL,
goal text,
agent_privileges smallint[] DEFAULT '{1,2}'::smallint[] NOT NULL,
- CONSTRAINT check_5aedde451d CHECK ((char_length(goal) <= 4096))
+ workflow_definition text DEFAULT 'software_development'::text NOT NULL,
+ CONSTRAINT check_5aedde451d CHECK ((char_length(goal) <= 4096)),
+ CONSTRAINT check_ec723e2a1a CHECK ((char_length(workflow_definition) <= 255))
);
CREATE SEQUENCE duo_workflows_workflows_id_seq
@@ -22773,8 +22775,7 @@ CREATE TABLE workspaces (
namespace text NOT NULL,
desired_state text NOT NULL,
actual_state text NOT NULL,
- devfile_ref text NOT NULL,
- devfile_path text NOT NULL,
+ devfile_path text,
devfile text,
processed_devfile text,
url text,
@@ -22785,15 +22786,17 @@ CREATE TABLE workspaces (
url_query_string text,
workspaces_agent_config_version integer NOT NULL,
desired_config_generator_version integer,
+ project_ref text,
CONSTRAINT check_15543fb0fa CHECK ((char_length(name) <= 64)),
CONSTRAINT check_157d5f955c CHECK ((char_length(namespace) <= 64)),
CONSTRAINT check_2b401b0034 CHECK ((char_length(deployment_resource_version) <= 64)),
CONSTRAINT check_35e31ca320 CHECK ((desired_config_generator_version IS NOT NULL)),
+ CONSTRAINT check_72fee08424 CHECK ((char_length(project_ref) <= 256)),
CONSTRAINT check_77d1a2ff50 CHECK ((char_length(processed_devfile) <= 65535)),
CONSTRAINT check_8a0ab61b6b CHECK ((char_length(url_query_string) <= 256)),
- CONSTRAINT check_8e363ee3ad CHECK ((char_length(devfile_ref) <= 256)),
CONSTRAINT check_8e4db5ffc2 CHECK ((char_length(actual_state) <= 32)),
CONSTRAINT check_9e42558c35 CHECK ((char_length(url) <= 1024)),
+ CONSTRAINT check_a758efdc89 CHECK ((project_ref IS NOT NULL)),
CONSTRAINT check_b70eddcbc1 CHECK ((char_length(desired_state) <= 32)),
CONSTRAINT check_dc58d56169 CHECK ((char_length(devfile_path) <= 2048)),
CONSTRAINT check_eb32879a3d CHECK ((char_length(devfile) <= 65535)),
diff --git a/doc/administration/geo/replication/troubleshooting/common.md b/doc/administration/geo/replication/troubleshooting/common.md
index a7f98ed2882..2a0082eb6f7 100644
--- a/doc/administration/geo/replication/troubleshooting/common.md
+++ b/doc/administration/geo/replication/troubleshooting/common.md
@@ -671,3 +671,50 @@ Gitlab::HTTP.get(primary.internal_uri, allow_local_requests: true, limit: 10)
Make sure that the value of `internal_uri` is correct in the output above.
If the URL of the primary site is incorrect, double-check it in `/etc/gitlab/gitlab.rb`, and in **Admin > Geo > Sites**.
+
+### Excessive database IO from Geo metrics collection
+
+If you're experiencing high database load due to frequent Geo metrics collection, you can reduce the frequency of the `geo_metrics_update_worker` job. This adjustment can help alleviate database strain in large GitLab instances where metrics collection significantly impacts database performance.
+
+Increasing the interval means that your Geo metrics are updated less frequently. This results in metrics being out-of-date for longer periods of time, which may impact your ability to monitor Geo replication in real-time. If metrics are out-of-date for more than 10 minutes, the site is arbitrarily marked as "Unhealthy" in the Admin Area.
+
+The following example sets the job to run every 30 minutes. Adjust the cron schedule based on your needs.
+
+::Tabs
+
+:::TabTitle Linux package (Omnibus)
+
+1. Add or modify the following setting in `/etc/gitlab/gitlab.rb`:
+
+ ```ruby
+ gitlab_rails['geo_metrics_update_worker_cron'] = "*/30 * * * *"
+ ```
+
+1. Reconfigure GitLab:
+
+ ```shell
+ sudo gitlab-ctl reconfigure
+ ```
+
+:::TabTitle Self-compiled (source)
+
+1. Edit `/home/git/gitlab/config/gitlab.yml`:
+
+ ```yaml
+ production: &base
+ ee_cron_jobs:
+ geo_metrics_update_worker:
+ cron: "*/30 * * * *"
+ ```
+
+1. Save the file and restart GitLab:
+
+ ```shell
+ # For systems running systemd
+ sudo systemctl restart gitlab.target
+
+ # For systems running SysV init
+ sudo service gitlab restart
+ ```
+
+::EndTabs
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index a034107b3c1..f8633332bdb 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -11728,11 +11728,12 @@ Input type: `WorkspaceCreateInput`
| `clientMutationId` | [`String`](#string) | A unique identifier for the client performing the mutation. |
| `clusterAgentId` | [`ClustersAgentID!`](#clustersagentid) | GlobalID of the cluster agent the created workspace will be associated with. |
| `desiredState` | [`String!`](#string) | Desired state of the created workspace. |
-| `devfilePath` | [`String!`](#string) | Project repo git path containing the devfile used to configure the workspace. |
-| `devfileRef` | [`String!`](#string) | Project repo git ref containing the devfile used to configure the workspace. |
+| `devfilePath` | [`String`](#string) | Project path containing the devfile used to configure the workspace. If not provided, the GitLab default devfile is used. |
+| `devfileRef` **{warning-solid}** | [`String`](#string) | **Deprecated:** Argument is renamed to project_ref. Deprecated in GitLab 17.8. |
| `editor` **{warning-solid}** | [`String`](#string) | **Deprecated:** Argument is not used. Deprecated in GitLab 17.5. |
| `maxHoursBeforeTermination` | [`Int!`](#int) | Maximum hours the workspace can exist before it is automatically terminated. |
| `projectId` | [`ProjectID!`](#projectid) | ID of the project that will provide the Devfile for the created workspace. |
+| `projectRef` | [`String`](#string) | Project repo git ref. |
| `variables` | [`[WorkspaceVariableInput!]`](#workspacevariableinput) | Variables to inject into the workspace. |
#### Fields
@@ -23650,6 +23651,7 @@ A Duo Workflow.
| `projectId` | [`ProjectID!`](#projectid) | ID of the project. |
| `updatedAt` | [`Time!`](#time) | Timestamp of when the workflow was last updated. |
| `userId` | [`UserID!`](#userid) | ID of the user. |
+| `workflowDefinition` | [`String`](#string) | Duo Workflow type based on its capabilities. |
### `DuoWorkflowEnablement`
@@ -23687,6 +23689,7 @@ Events that describe the history and progress of a Duo Workflow.
| `metadata` | [`JsonString`](#jsonstring) | Metadata associated with the event. |
| `parentTimestamp` | [`Time`](#time) | Time of the parent event. |
| `timestamp` | [`Time`](#time) | Time of the event. |
+| `workflowDefinition` | [`String`](#string) | Duo Workflow type based on its capabilities. |
| `workflowGoal` | [`String`](#string) | Goal of the workflow. |
| `workflowStatus` | [`DuoWorkflowStatus`](#duoworkflowstatus) | Status of the workflow. |
@@ -26110,6 +26113,7 @@ four standard [pagination arguments](#pagination-arguments):
| `sort` | [`NamespaceProjectSort`](#namespaceprojectsort) | Sort projects by the criteria. |
| `withIssuesEnabled` | [`Boolean`](#boolean) | Return only projects with issues enabled. |
| `withMergeRequestsEnabled` | [`Boolean`](#boolean) | Return only projects with merge requests enabled. |
+| `withNamespaceDomainPages` | [`Boolean`](#boolean) | Return only projects that use the namespace domain for pages projects. |
##### `Group.releases`
@@ -30352,6 +30356,7 @@ four standard [pagination arguments](#pagination-arguments):
| `sort` | [`NamespaceProjectSort`](#namespaceprojectsort) | Sort projects by the criteria. |
| `withIssuesEnabled` | [`Boolean`](#boolean) | Return only projects with issues enabled. |
| `withMergeRequestsEnabled` | [`Boolean`](#boolean) | Return only projects with merge requests enabled. |
+| `withNamespaceDomainPages` | [`Boolean`](#boolean) | Return only projects that use the namespace domain for pages projects. |
##### `Namespace.remoteDevelopmentClusterAgents`
@@ -38361,9 +38366,9 @@ Represents a remote development workspace.
| `desiredState` | [`String!`](#string) | Desired state of the workspace. |
| `desiredStateUpdatedAt` | [`Time!`](#time) | Timestamp of the last update to the desired state. |
| `devfile` | [`String!`](#string) | Source YAML of the devfile used to configure the workspace. |
-| `devfilePath` | [`String!`](#string) | Path to the devfile used to configure the workspace. |
-| `devfileRef` | [`String!`](#string) | Git reference that contains the devfile used to configure the workspace. |
-| `devfileWebUrl` | [`String!`](#string) | Web URL of the devfile used to configure the workspace. |
+| `devfilePath` | [`String`](#string) | Path to the devfile used to configure the workspace. |
+| `devfileRef` **{warning-solid}** | [`String!`](#string) | **Deprecated** in GitLab 17.8. Field is renamed to project_ref. |
+| `devfileWebUrl` **{warning-solid}** | [`String`](#string) | **Deprecated** in GitLab 17.8. Field is not used. |
| `editor` **{warning-solid}** | [`String!`](#string) | **Deprecated** in GitLab 17.5. Field is not used. |
| `forceIncludeAllResources` **{warning-solid}** | [`Boolean!`](#boolean) | **Introduced** in GitLab 17.6. **Status**: Experiment. Forces all resources to be included for the workspaceduring the next reconciliation with the agent. |
| `id` | [`RemoteDevelopmentWorkspaceID!`](#remotedevelopmentworkspaceid) | Global ID of the workspace. |
@@ -38372,6 +38377,7 @@ Represents a remote development workspace.
| `namespace` | [`String!`](#string) | Namespace of the workspace in Kubernetes. |
| `processedDevfile` | [`String!`](#string) | Processed YAML of the devfile used to configure the workspace. |
| `projectId` | [`ID!`](#id) | ID of the project that contains the devfile for the workspace. |
+| `projectRef` | [`String!`](#string) | Git reference that contains the devfile used to configure the workspace, and that will be cloned into the workspace. |
| `respondedToAgentAt` | [`Time`](#time) | Timestamp of the last response sent to the GitLab agent for Kubernetes for the workspace. |
| `updatedAt` | [`Time!`](#time) | Timestamp of the last update to any mutable workspace property. |
| `url` | [`String!`](#string) | URL of the workspace. |
diff --git a/doc/user/custom_roles/abilities.md b/doc/user/custom_roles/abilities.md
index 2e9c3b10f80..d19bac3ba81 100644
--- a/doc/user/custom_roles/abilities.md
+++ b/doc/user/custom_roles/abilities.md
@@ -25,101 +25,101 @@ Any dependencies are noted in the `Description` column for each permission.
## Admin
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`read_admin_dashboard`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171581) | Read-only access to admin dashboard | GitLab [17.6](https://gitlab.com/gitlab-org/gitlab/-/issues/501549) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Read-only access to admin dashboard | Read-only access to admin dashboard | [`read_admin_dashboard`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/171581) | Instance | GitLab [17.6](https://gitlab.com/gitlab-org/gitlab/-/issues/501549) |
## Code review workflow
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`manage_merge_request_settings`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151586) | Configure merge request settings at the group or project level. Group actions include managing merge checks and approval settings. Project actions include managing MR configurations, approval rules and settings, and branch targets. In order to enable Suggested reviewers, the "Manage project access tokens" custom permission needs to be enabled. | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/443235) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Manage merge request approvals and settings | Configure merge request settings at the group or project level. Group actions include managing merge checks and approval settings. Project actions include managing MR configurations, approval rules and settings, and branch targets. In order to enable Suggested reviewers, the "Manage project access tokens" custom permission needs to be enabled. | [`manage_merge_request_settings`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151586) | Group,
Project | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/443235) |
## Compliance management
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`admin_compliance_framework`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144183) | Create, read, update, and delete compliance frameworks. Users with this permission can also assign a compliance framework label to a project, and set the default framework of a group. | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/411502) | | |
-| [`read_compliance_dashboard`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175066) | Read compliance capabilities including adherence, violations, and frameworks for groups and projects. | GitLab [17.7](https://gitlab.com/gitlab-org/gitlab/-/issues/465324) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Manage and assign compliance frameworks | Create, read, update, and delete compliance frameworks. Users with this permission can also assign a compliance framework label to a project, and set the default framework of a group. | [`admin_compliance_framework`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/144183) | Group,
Project | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/411502) |
+| Read compliance dashboard | Read compliance capabilities including adherence, violations, and frameworks for groups and projects. | [`read_compliance_dashboard`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/175066) | Group,
Project | GitLab [17.7](https://gitlab.com/gitlab-org/gitlab/-/issues/465324) |
## Continuous delivery
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`manage_deploy_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151677) | Manage deploy tokens at the group or project level. | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/448843) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Manage deploy tokens | Manage deploy tokens at the group or project level. | [`manage_deploy_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151677) | Group,
Project | GitLab [17.0](https://gitlab.com/gitlab-org/gitlab/-/issues/448843) |
## Groups and projects
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`admin_group_member`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131914) | Add or remove users in a group, and assign roles to users. When assigning a role, users with this custom permission must select a role that has the same or fewer permissions as the default role used as the base for their custom role. | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/17364) | | |
-| [`archive_project`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134998) | Allows archiving of projects. | GitLab [16.6](https://gitlab.com/gitlab-org/gitlab/-/issues/425957) | | |
-| [`remove_group`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145166) | Ability to delete or restore a group. This ability does not allow deleting top-level groups. Review the Retention period settings to prevent accidental deletion. | GitLab [16.10](https://gitlab.com/gitlab-org/gitlab/-/issues/425962) | | |
-| [`remove_project`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139696) | Allows deletion of projects. | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/425959) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Manage group members | Add or remove users in a group, and assign roles to users. When assigning a role, users with this custom permission must select a role that has the same or fewer permissions as the default role used as the base for their custom role. | [`admin_group_member`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/131914) | Group | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/17364) |
+| Archive project | Allows archiving of projects. | [`archive_project`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134998) | Project | GitLab [16.6](https://gitlab.com/gitlab-org/gitlab/-/issues/425957) |
+| Delete group | Ability to delete or restore a group. This ability does not allow deleting top-level groups. Review the Retention period settings to prevent accidental deletion. | [`remove_group`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/145166) | Group | GitLab [16.10](https://gitlab.com/gitlab-org/gitlab/-/issues/425962) |
+| Delete project | Allows deletion of projects. | [`remove_project`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139696) | Project | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/425959) |
## Infrastructure as code
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`admin_terraform_state`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140759) | Execute terraform commands, lock/unlock terraform state files, and remove file versions. | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/421789) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Manage Terraform state | Execute terraform commands, lock/unlock terraform state files, and remove file versions. | [`admin_terraform_state`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140759) | Project | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/421789) |
## Integrations
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`admin_integrations`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154601) | Create, read, update, and delete integrations with external applications. | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/460522) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Manage integrations | Create, read, update, and delete integrations with external applications. | [`admin_integrations`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154601) | Group,
Project | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/460522) |
## Runner
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`admin_runners`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151825) | Create, view, edit, and delete group or project Runners. Includes configuring Runner settings. | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/442851) | | |
-| [`read_runners`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156798) | Allows read-only access to group or project runners, including the runner fleet dashboard. | GitLab [17.2](https://gitlab.com/gitlab-org/gitlab/-/issues/468202) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Manage runners | Create, view, edit, and delete group or project Runners. Includes configuring Runner settings. | [`admin_runners`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151825) | Group,
Project | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/442851) |
+| View runners | Allows read-only access to group or project runners, including the runner fleet dashboard. | [`read_runners`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/156798) | Group,
Project | GitLab [17.2](https://gitlab.com/gitlab-org/gitlab/-/issues/468202) |
## Secrets management
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`admin_cicd_variables`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143369) | Create, read, update, and delete CI/CD variables. | GitLab [16.10](https://gitlab.com/gitlab-org/gitlab/-/issues/437947) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Manage CI/CD variables | Create, read, update, and delete CI/CD variables. | [`admin_cicd_variables`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143369) | Group,
Project | GitLab [16.10](https://gitlab.com/gitlab-org/gitlab/-/issues/437947) |
## Security policy management
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`manage_security_policy_link`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148371) | Allows linking security policy projects. | GitLab [16.11](https://gitlab.com/gitlab-org/gitlab/-/issues/440226) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Link to a security policy project | Allows linking security policy projects. | [`manage_security_policy_link`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/148371) | Group,
Project | GitLab [16.11](https://gitlab.com/gitlab-org/gitlab/-/issues/440226) |
## Source code management
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`admin_merge_request`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128302) | Allows approval of merge requests. | GitLab [16.4](https://gitlab.com/gitlab-org/gitlab/-/issues/412708) | | |
-| [`admin_protected_branch`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/162208) | Create, read, update, and delete protected branches for a project. | GitLab [17.4](https://gitlab.com/gitlab-org/gitlab/-/issues/448823) | | |
-| [`admin_push_rules`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147872) | Configure push rules for repositories at the group or project level. | GitLab [16.11](https://gitlab.com/gitlab-org/gitlab/-/issues/421786) | | |
-| [`read_code`](https://gitlab.com/gitlab-org/gitlab/-/issues/376180) | Allows read-only access to the source code in the user interface. Does not allow users to edit or download repository archives, clone or pull repositories, view source code in an IDE, or view merge requests for private projects. You can download individual files because read-only access inherently grants the ability to make a local copy of the file. | GitLab [15.7](https://gitlab.com/gitlab-org/gitlab/-/issues/20277) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Approve merge request | Allows approval of merge requests. | [`admin_merge_request`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128302) | Project | GitLab [16.4](https://gitlab.com/gitlab-org/gitlab/-/issues/412708) |
+| | Create, read, update, and delete protected branches for a project. | [`admin_protected_branch`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/162208) | Project | GitLab [17.4](https://gitlab.com/gitlab-org/gitlab/-/issues/448823) |
+| Manage push rules | Configure push rules for repositories at the group or project level. | [`admin_push_rules`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/147872) | Group,
Project | GitLab [16.11](https://gitlab.com/gitlab-org/gitlab/-/issues/421786) |
+| View repository code | Allows read-only access to the source code in the user interface. Does not allow users to edit or download repository archives, clone or pull repositories, view source code in an IDE, or view merge requests for private projects. You can download individual files because read-only access inherently grants the ability to make a local copy of the file. | [`read_code`](https://gitlab.com/gitlab-org/gitlab/-/issues/376180) | Group,
Project | GitLab [15.7](https://gitlab.com/gitlab-org/gitlab/-/issues/20277) |
## System access
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`manage_group_access_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140115) | Create, read, update, and delete group access tokens. When creating a token, users with this custom permission must select a role for that token that has the same or fewer permissions as the default role used as the base for the custom role. | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/428353) | | |
-| [`manage_project_access_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132342) | Create, read, update, and delete project access tokens. When creating a token, users with this custom permission must select a role for that token that has the same or fewer permissions as the default role used as the base for the custom role. | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/421778) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Manage group access tokens | Create, read, update, and delete group access tokens. When creating a token, users with this custom permission must select a role for that token that has the same or fewer permissions as the default role used as the base for the custom role. | [`manage_group_access_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140115) | Group | GitLab [16.8](https://gitlab.com/gitlab-org/gitlab/-/issues/428353) |
+| Manage project access tokens | Create, read, update, and delete project access tokens. When creating a token, users with this custom permission must select a role for that token that has the same or fewer permissions as the default role used as the base for the custom role. | [`manage_project_access_tokens`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/132342) | Project | GitLab [16.5](https://gitlab.com/gitlab-org/gitlab/-/issues/421778) |
## Team planning
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`read_crm_contact`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154017) | Read CRM contact. | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/443268) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| View CRM contact | Read CRM contact. | [`read_crm_contact`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/154017) | Group | GitLab [17.1](https://gitlab.com/gitlab-org/gitlab/-/issues/443268) |
## Vulnerability management
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`admin_vulnerability`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121534) | Edit the vulnerability object, including the status and linking an issue. Includes the `read_vulnerability` permission actions. | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/412536) | | |
-| [`read_dependency`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126247) | Allows read-only access to the dependencies and licenses. | GitLab [16.3](https://gitlab.com/gitlab-org/gitlab/-/issues/415255) | | |
-| [`read_vulnerability`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120704) | Read vulnerability reports and security dashboards. | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/399119) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Manage vulnerabilities | Edit the vulnerability object, including the status and linking an issue. Includes the `read_vulnerability` permission actions. | [`admin_vulnerability`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121534) | Group,
Project | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/412536) |
+| View dependency list | Allows read-only access to the dependencies and licenses. | [`read_dependency`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/126247) | Group,
Project | GitLab [16.3](https://gitlab.com/gitlab-org/gitlab/-/issues/415255) |
+| View vulnerability reports and dashboards | Read vulnerability reports and security dashboards. | [`read_vulnerability`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/120704) | Group,
Project | GitLab [16.1](https://gitlab.com/gitlab-org/gitlab/-/issues/399119) |
## Webhooks
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
-| [`admin_web_hook`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151551) | Manage webhooks | GitLab [17.0](https://gitlab.com/gitlab-org/quality/triage-ops/-/issues/1373) | | |
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
+| Manage web hooks | Manage webhooks | [`admin_web_hook`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/151551) | Group,
Project | GitLab [17.0](https://gitlab.com/gitlab-org/quality/triage-ops/-/issues/1373) |
diff --git a/lib/gitlab/http_io.rb b/lib/gitlab/http_io.rb
index 25b86fbf22f..f48017a74ca 100644
--- a/lib/gitlab/http_io.rb
+++ b/lib/gitlab/http_io.rb
@@ -75,8 +75,12 @@ module Gitlab
end
end
+ # https://www.rubydoc.info/stdlib/core/IO:read says:
+ # When this method is called at end of file, it returns nil or "",
+ # depending on length: read, read(nil), and read(0) return "",
+ # read(positive_integer) returns nil.
def read(length = nil, outbuf = nil)
- out = []
+ out = length&.positive? ? nil : []
length ||= size - tell
@@ -87,15 +91,16 @@ module Gitlab
chunk_bytes = [BUFFER_SIZE - chunk_offset, length].min
data_slice = data.byteslice(0, chunk_bytes)
+ out ||= []
out << data_slice
@tell += data_slice.bytesize
length -= data_slice.bytesize
end
- out = out.join
+ out = out&.join
# If outbuf is passed, we put the output into the buffer. This supports IO.copy_stream functionality
- if outbuf
+ if outbuf && out
outbuf.replace(out)
end
diff --git a/lib/gitlab/topology_service_client/base_service.rb b/lib/gitlab/topology_service_client/base_service.rb
index 3cc024b6462..dbdbdb9d984 100644
--- a/lib/gitlab/topology_service_client/base_service.rb
+++ b/lib/gitlab/topology_service_client/base_service.rb
@@ -32,7 +32,7 @@ module Gitlab
end
def enabled?
- Gitlab.config.topology_service_enabled? && Gitlab.config.has_configured_cell?
+ Gitlab.config.topology_service_enabled?
end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index 337c6c5c03b..7798fe4bde4 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -39520,6 +39520,9 @@ msgstr ""
msgid "Organization|Home organization"
msgstr ""
+msgid "Organization|If you proceed with this change you will lose your owner permissions for this organization, including access to this page."
+msgstr ""
+
msgid "Organization|Internal - The organization can be accessed by any signed in user except external users."
msgstr ""
diff --git a/spec/features/projects/network_graph_spec.rb b/spec/features/projects/network_graph_spec.rb
index f7c0e9ad328..e84bbf382ad 100644
--- a/spec/features/projects/network_graph_spec.rb
+++ b/spec/features/projects/network_graph_spec.rb
@@ -11,8 +11,6 @@ RSpec.describe 'Project Network Graph', :js, feature_category: :groups_and_proje
before do
sign_in(user)
- stub_feature_flags(use_list_commits_rpc_network_graph: false)
-
project.repository.create_branch(ref_with_hash, 'master')
# Stub Graph max_size to speed up test (10 commits vs. 650)
diff --git a/spec/finders/personal_access_tokens_finder_spec.rb b/spec/finders/personal_access_tokens_finder_spec.rb
index 567b089d538..a3be9cae547 100644
--- a/spec/finders/personal_access_tokens_finder_spec.rb
+++ b/spec/finders/personal_access_tokens_finder_spec.rb
@@ -241,6 +241,38 @@ RSpec.describe PersonalAccessTokensFinder, :enable_admin_mode, feature_category:
end
end
+ describe 'by expires before' do
+ where(:by_expires_before, :expected_tokens) do
+ 2.days.ago | []
+ 29.days.from_now | [:expired, :expired_impersonation]
+ 30.days.from_now | ref(:tokens_keys)
+ end
+
+ with_them do
+ let(:params) { { expires_before: by_expires_before } }
+
+ it 'returns tokens by expires before' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
+ end
+ end
+ end
+
+ describe 'by expires after' do
+ where(:by_expires_after, :expected_tokens) do
+ 2.days.ago | ref(:tokens_keys)
+ 30.days.from_now | [:active, :active_other, :revoked, :active_impersonation, :revoked_impersonation, :bot]
+ 31.days.from_now | []
+ end
+
+ with_them do
+ let(:params) { { expires_after: by_expires_after } }
+
+ it 'returns tokens by expires after' do
+ is_expected.to match_array(tokens.values_at(*expected_tokens))
+ end
+ end
+ end
+
describe 'by last used date' do
before do
PersonalAccessToken.update_all(last_used_at: Time.now)
diff --git a/spec/frontend/organizations/users/components/user_details_drawer_spec.js b/spec/frontend/organizations/users/components/user_details_drawer_spec.js
index f608d7b0c1e..f1b9c3ba7c3 100644
--- a/spec/frontend/organizations/users/components/user_details_drawer_spec.js
+++ b/spec/frontend/organizations/users/components/user_details_drawer_spec.js
@@ -1,11 +1,11 @@
-import Vue from 'vue';
+import Vue, { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
-import { GlCollapsibleListbox, GlDrawer } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { GlAlert, GlCollapsibleListbox, GlDrawer } from '@gitlab/ui';
import organizationUserUpdateResponseWithErrors from 'test_fixtures/graphql/organizations/organization_user_update.mutation.graphql_with_errors.json';
import organizationUserUpdateResponse from 'test_fixtures/graphql/organizations/organization_user_update.mutation.graphql.json';
import organizationUserUpdateMutation from '~/organizations/users/graphql/mutations/organization_user_update.mutation.graphql';
import createMockApollo from 'helpers/mock_apollo_helper';
+import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
import { pageInfoMultiplePages } from 'jest/organizations/mock_data';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import UserDetailsDrawer from '~/organizations/users/components/user_details_drawer.vue';
@@ -31,16 +31,24 @@ describe('UserDetailsDrawer', () => {
const successfulResponseHandler = jest.fn().mockResolvedValue(organizationUserUpdateResponse);
const mockToastShow = jest.fn();
+ const findGlAlert = () => wrapper.findComponent(GlAlert);
const findGlDrawer = () => wrapper.findComponent(GlDrawer);
const findGlCollapsibleListbox = () => wrapper.findComponent(GlCollapsibleListbox);
const findUserAvatar = () => wrapper.findComponent(UserAvatar);
+ const findFooter = () => wrapper.findByTestId('user-details-drawer-footer');
+ const findSaveButton = () => wrapper.findByRole('button', { name: 'Save' });
+ const findCancelButton = () => wrapper.findByRole('button', { name: 'Cancel' });
const selectRole = (value) => findGlCollapsibleListbox().vm.$emit('select', value);
- const createComponent = ({ props = {}, handler = successfulResponseHandler } = {}) => {
+ const createComponent = ({
+ mountFn = shallowMountExtended,
+ props = {},
+ handler = successfulResponseHandler,
+ } = {}) => {
mockApollo = createMockApollo([[organizationUserUpdateMutation, handler]]);
- wrapper = shallowMount(UserDetailsDrawer, {
+ wrapper = mountFn(UserDetailsDrawer, {
propsData: {
user: mockUser,
pageInfo: pageInfoMultiplePages,
@@ -94,6 +102,12 @@ describe('UserDetailsDrawer', () => {
});
});
+ it('does not render footer and action buttons', () => {
+ expect(findFooter().exists()).toBe(false);
+ expect(findSaveButton().exists()).toBe(false);
+ expect(findCancelButton().exists()).toBe(false);
+ });
+
it('renders role listbox label', () => {
expect(findGlDrawer().text()).toContain('Organization role');
});
@@ -141,89 +155,157 @@ describe('UserDetailsDrawer', () => {
});
describe('when selecting new role', () => {
+ const unselectedRole =
+ mockUser.accessLevel.stringValue === ACCESS_LEVEL_OWNER_STRING
+ ? ACCESS_LEVEL_DEFAULT_STRING
+ : ACCESS_LEVEL_OWNER_STRING;
+
beforeEach(() => {
- createComponent();
- selectRole(ACCESS_LEVEL_DEFAULT_STRING);
+ createComponent({ mountFn: mountExtended });
+
+ selectRole(unselectedRole);
});
- it('calls GraphQL mutation with correct variables', () => {
- expect(successfulResponseHandler).toHaveBeenCalledWith({
- input: {
- id: mockUser.gid,
- accessLevel: ACCESS_LEVEL_DEFAULT_STRING,
- },
+ it('renders footer and action buttons', () => {
+ expect(findFooter().exists()).toBe(true);
+ expect(findSaveButton().exists()).toBe(true);
+ expect(findCancelButton().exists()).toBe(true);
+ });
+
+ it('does not render remove self as owner warning', () => {
+ expect(findGlAlert().exists()).toBe(false);
+ });
+
+ describe('when active user is the current user', () => {
+ beforeEach(() => {
+ window.gon.current_user_id = mockUser.id;
+ });
+
+ describe('when removing self as owner', () => {
+ beforeEach(() => {
+ createComponent({
+ props: {
+ user: {
+ ...mockUser,
+ accessLevel: { ...mockUser.accessLevel, stringValue: ACCESS_LEVEL_OWNER_STRING },
+ },
+ },
+ });
+
+ selectRole(ACCESS_LEVEL_DEFAULT_STRING);
+ });
+
+ it('renders remove self as owner warning', () => {
+ expect(findGlAlert().text()).toBe(
+ 'If you proceed with this change you will lose your owner permissions for this organization, including access to this page.',
+ );
+ });
});
});
- it('sets listbox to loading', () => {
- expect(findGlCollapsibleListbox().props('loading')).toBe(true);
- });
-
- it('emits loading start event', () => {
- expect(wrapper.emitted('loading')[0]).toEqual([true]);
- });
-
- describe('when role update is successful', () => {
+ describe('when save button is clicked', () => {
beforeEach(async () => {
- await waitForPromises();
+ await findSaveButton().trigger('click');
+ await nextTick();
});
- it('shows toast when GraphQL mutation is successful', () => {
- expect(mockToastShow).toHaveBeenCalledWith('Organization role was updated successfully.');
+ it('calls GraphQL mutation with correct variables', () => {
+ expect(successfulResponseHandler).toHaveBeenCalledWith({
+ input: {
+ id: mockUser.gid,
+ accessLevel: unselectedRole,
+ },
+ });
});
- it('emits role-change event', () => {
- expect(wrapper.emitted('role-change')).toHaveLength(1);
+ it('sets listbox to loading', () => {
+ expect(findGlCollapsibleListbox().props('loading')).toBe(true);
});
- it('emits loading end event', () => {
- expect(wrapper.emitted('loading')[1]).toEqual([false]);
+ it('emits loading start event', () => {
+ expect(wrapper.emitted('loading')[0]).toEqual([true]);
});
- });
- describe('when role update has a validation error', () => {
- beforeEach(async () => {
- const errorResponseHandler = jest
- .fn()
- .mockResolvedValue(organizationUserUpdateResponseWithErrors);
-
- createComponent({
- handler: errorResponseHandler,
- props: { user: { ...mockUser, accessLevel: ACCESS_LEVEL_OWNER_STRING } },
+ describe('when role update is successful', () => {
+ beforeEach(async () => {
+ await waitForPromises();
});
- selectRole(ACCESS_LEVEL_DEFAULT_STRING);
- await waitForPromises();
+ it('shows toast when GraphQL mutation is successful', () => {
+ expect(mockToastShow).toHaveBeenCalledWith(
+ 'Organization role was updated successfully.',
+ );
+ });
+
+ it('emits role-change event', () => {
+ expect(wrapper.emitted('role-change')).toHaveLength(1);
+ });
+
+ it('emits loading end event', () => {
+ expect(wrapper.emitted('loading')[1]).toEqual([false]);
+ });
});
- it('creates an alert', () => {
- expect(createAlert).toHaveBeenCalledWith({
- message: 'You cannot change the access of the last owner from the organization',
+ describe('when role update has a validation error', () => {
+ beforeEach(async () => {
+ const errorResponseHandler = jest
+ .fn()
+ .mockResolvedValue(organizationUserUpdateResponseWithErrors);
+
+ createComponent({
+ mountFn: mountExtended,
+ handler: errorResponseHandler,
+ });
+
+ selectRole(unselectedRole);
+ await nextTick();
+
+ await findSaveButton().trigger('click');
+ await waitForPromises();
+ });
+
+ it('creates an alert', () => {
+ expect(createAlert).toHaveBeenCalledWith({
+ message: 'You cannot change the access of the last owner from the organization',
+ });
+ });
+ });
+
+ describe('when role update has a network error', () => {
+ const error = new Error();
+
+ beforeEach(async () => {
+ const errorResponseHandler = jest.fn().mockRejectedValue(error);
+
+ createComponent({
+ mountFn: mountExtended,
+ handler: errorResponseHandler,
+ });
+
+ selectRole(unselectedRole);
+ await nextTick();
+
+ await findSaveButton().trigger('click');
+ await waitForPromises();
+ });
+
+ it('creates an alert', () => {
+ expect(createAlert).toHaveBeenCalledWith({
+ message: 'An error occurred updating the organization role. Please try again.',
+ error,
+ captureError: true,
+ });
});
});
});
- describe('when role update has a network error', () => {
- const error = new Error();
+ describe('when cancel button is clicked', () => {
+ it('resets listbox to the initial user role', async () => {
+ await findCancelButton().trigger('click');
- beforeEach(async () => {
- const errorResponseHandler = jest.fn().mockRejectedValue(error);
-
- createComponent({
- handler: errorResponseHandler,
- props: { user: { ...mockUser, accessLevel: ACCESS_LEVEL_OWNER_STRING } },
- });
-
- selectRole(ACCESS_LEVEL_DEFAULT_STRING);
- await waitForPromises();
- });
-
- it('creates an alert', () => {
- expect(createAlert).toHaveBeenCalledWith({
- message: 'An error occurred updating the organization role. Please try again.',
- error,
- captureError: true,
- });
+ expect(findGlCollapsibleListbox().props('selected')).toBe(
+ mockUser.accessLevel.stringValue,
+ );
});
});
});
diff --git a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
index 3ddfc0bf757..46e1092e27e 100644
--- a/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
+++ b/spec/graphql/resolvers/namespace_projects_resolver_spec.rb
@@ -18,7 +18,8 @@ RSpec.describe Resolvers::NamespaceProjectsResolver, feature_category: :groups_a
sort: nil,
ids: nil,
with_issues_enabled: nil,
- with_merge_requests_enabled: nil
+ with_merge_requests_enabled: nil,
+ with_namespace_domain_pages: nil
}
end
@@ -121,6 +122,23 @@ RSpec.describe Resolvers::NamespaceProjectsResolver, feature_category: :groups_a
it { is_expected.to contain_exactly(*group_namespaced_projects) }
end
end
+
+ context 'with_namespace_domain_pages' do
+ before do
+ group_namespaced_projects[0...-1].each do |project|
+ project.project_setting.update!(pages_unique_domain_enabled: false)
+ end
+ group_namespaced_projects.last.project_setting.update!(
+ pages_unique_domain_enabled: true,
+ pages_unique_domain: 'foo123.example.com'
+ )
+ end
+
+ let(:args) { default_args.merge(with_namespace_domain_pages: true) }
+ let(:expected_projects) { group_namespaced_projects[0...-1] }
+
+ it { is_expected.to contain_exactly(*expected_projects) }
+ end
end
context 'with an user namespace' do
diff --git a/spec/initializers/validate_cell_config_spec.rb b/spec/initializers/validate_cell_config_spec.rb
new file mode 100644
index 00000000000..dc68c649254
--- /dev/null
+++ b/spec/initializers/validate_cell_config_spec.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe 'validate database config', feature_category: :cell do
+ include StubENV
+
+ let(:rails_configuration) { Rails::Application::Configuration.new(Rails.root) }
+
+ subject(:validate_config) do
+ load Rails.root.join('config/initializers/validate_cell_config.rb')
+ end
+
+ before do
+ allow(Rails.application).to receive(:config).and_return(rails_configuration)
+ end
+
+ shared_examples 'with SKIP_CELL_CONFIG_VALIDATION=true' do
+ before do
+ stub_env('SKIP_CELL_CONFIG_VALIDATION', 'true')
+ end
+
+ it 'does not raise exception' do
+ expect { validate_config }.not_to raise_error
+ end
+ end
+
+ context 'when topology service is correctly configured' do
+ before do
+ stub_config(cell: { id: 1 }, topology_service: { enabled: true })
+ end
+
+ it 'does not raise exception' do
+ expect { validate_config }.not_to raise_error
+ end
+ end
+
+ context 'when topology service is not configured' do
+ before do
+ stub_config(cell: { id: nil }, topology_service: { enabled: false })
+ end
+
+ it 'does not raise exception' do
+ expect { validate_config }.not_to raise_error
+ end
+ end
+
+ context 'when configuration is wrong' do
+ context 'when only cell.id is configured' do
+ before do
+ stub_config(cell: { id: 1 }, topology_service: { enabled: false })
+ end
+
+ it 'does not raise exception' do
+ expect { validate_config }.to raise_error("Topology Service is not configured, but Cell ID is set")
+ end
+
+ it_behaves_like 'with SKIP_CELL_CONFIG_VALIDATION=true'
+ end
+
+ context 'when only topology service is enabled' do
+ before do
+ stub_config(cell: { id: nil }, topology_service: { enabled: true })
+ end
+
+ it 'does not raise exception' do
+ expect { validate_config }.to raise_error("Topology Service is enabled, but Cell ID is not set")
+ end
+
+ it_behaves_like 'with SKIP_CELL_CONFIG_VALIDATION=true'
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
index 3f6727e5ab4..e53794d81be 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
@@ -148,6 +148,26 @@ RSpec.describe Gitlab::Ci::Build::Artifacts::Metadata, feature_category: :job_ar
it 'raises error' do
expect { find_entries }.to raise_error(described_class::InvalidStreamError, /not in gzip format/)
end
+
+ context 'when metadata is an HttpIO stream' do
+ let(:tmpfile) { Tempfile.new('test-metadata') }
+ let(:url) { "file://#{tmpfile.path}" }
+ let(:metadata_file_stream) { Gitlab::HttpIO.new(url, 0) }
+
+ before do
+ # Normally file:// URLs are not allowed, but bypass this for the sake of testing
+ # so we don't have to run a Web server.
+ allow(::Gitlab::UrlSanitizer).to receive(:valid?).with(url).and_return(true)
+ end
+
+ after do
+ tmpfile.unlink
+ end
+
+ it 'raises error' do
+ expect { find_entries }.to raise_error(described_class::InvalidStreamError, /not in gzip format/)
+ end
+ end
end
context 'with generated metadata' do
diff --git a/spec/lib/gitlab/http_io_spec.rb b/spec/lib/gitlab/http_io_spec.rb
index 1376b726df3..d47e69ae815 100644
--- a/spec/lib/gitlab/http_io_spec.rb
+++ b/spec/lib/gitlab/http_io_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Gitlab::HttpIO do
+RSpec.describe Gitlab::HttpIO, feature_category: :shared do
include HttpIOHelpers
let(:http_io) { described_class.new(url, size) }
@@ -122,7 +122,24 @@ RSpec.describe Gitlab::HttpIO do
describe '#read' do
subject { http_io.read(length) }
+ shared_examples 'reads the body' do
+ let(:expected_outbuf) { expected_body || "" }
+
+ it 'reads a trace' do
+ is_expected.to eq(expected_body)
+ end
+
+ it 'reads with outbuf' do
+ buf = +""
+
+ expect(http_io.read(length, buf)).to eq(expected_body)
+ expect(buf).to eq(expected_outbuf)
+ end
+ end
+
context 'when there are no network issue' do
+ let(:expected_body) { file_body }
+
before do
stub_remote_url_206(url, file_path)
end
@@ -135,9 +152,7 @@ RSpec.describe Gitlab::HttpIO do
set_smaller_buffer_size_than(size)
end
- it 'reads a trace' do
- is_expected.to eq(file_body)
- end
+ it_behaves_like 'reads the body'
end
context 'when BUFFER_SIZE is larger than file size' do
@@ -145,23 +160,20 @@ RSpec.describe Gitlab::HttpIO do
set_larger_buffer_size_than(size)
end
- it 'reads a trace' do
- is_expected.to eq(file_body)
- end
+ it_behaves_like 'reads the body'
end
end
context 'when read only first 100 bytes' do
let(:length) { 100 }
+ let(:expected_body) { file_body[0, length] }
context 'when BUFFER_SIZE is smaller than file size' do
before do
set_smaller_buffer_size_than(size)
end
- it 'reads a trace' do
- is_expected.to eq(file_body[0, length])
- end
+ it_behaves_like 'reads the body'
end
context 'when BUFFER_SIZE is larger than file size' do
@@ -169,23 +181,20 @@ RSpec.describe Gitlab::HttpIO do
set_larger_buffer_size_than(size)
end
- it 'reads a trace' do
- is_expected.to eq(file_body[0, length])
- end
+ it_behaves_like 'reads the body'
end
end
context 'when tries to read oversize' do
let(:length) { size + 1000 }
+ let(:expected_body) { file_body }
context 'when BUFFER_SIZE is smaller than file size' do
before do
set_smaller_buffer_size_than(size)
end
- it 'reads a trace' do
- is_expected.to eq(file_body)
- end
+ it_behaves_like 'reads the body'
end
context 'when BUFFER_SIZE is larger than file size' do
@@ -193,23 +202,20 @@ RSpec.describe Gitlab::HttpIO do
set_larger_buffer_size_than(size)
end
- it 'reads a trace' do
- is_expected.to eq(file_body)
- end
+ it_behaves_like 'reads the body'
end
end
context 'when tries to read 0 bytes' do
let(:length) { 0 }
+ let(:expected_body) { "" }
context 'when BUFFER_SIZE is smaller than file size' do
before do
set_smaller_buffer_size_than(size)
end
- it 'reads a trace' do
- is_expected.to be_empty
- end
+ it_behaves_like 'reads the body'
end
context 'when BUFFER_SIZE is larger than file size' do
@@ -217,14 +223,30 @@ RSpec.describe Gitlab::HttpIO do
set_larger_buffer_size_than(size)
end
- it 'reads a trace' do
- is_expected.to be_empty
- end
+ it_behaves_like 'reads the body'
end
end
end
- context 'when there is anetwork issue' do
+ context 'when current pos is at end of the file' do
+ before do
+ http_io.seek(size, IO::SEEK_SET)
+ end
+
+ it 'returns nil when attempting to read a byte' do
+ expect(http_io.read(1)).to be_nil
+ end
+
+ it 'returns "" when attempting to read 0 bytes' do
+ expect(http_io.read(0)).to eq("")
+ end
+
+ it 'returns "" when attempting to read' do
+ expect(http_io.read).to eq("")
+ end
+ end
+
+ context 'when there is a network issue' do
let(:length) { nil }
before do
diff --git a/spec/lib/gitlab/topology_service_client/base_service_spec.rb b/spec/lib/gitlab/topology_service_client/base_service_spec.rb
index ec4392160ca..d52586a09fe 100644
--- a/spec/lib/gitlab/topology_service_client/base_service_spec.rb
+++ b/spec/lib/gitlab/topology_service_client/base_service_spec.rb
@@ -12,13 +12,6 @@ RSpec.describe Gitlab::TopologyServiceClient::BaseService, feature_category: :ce
expect { base_service }.to raise_error(NotImplementedError)
end
-
- it 'raises an error when no cell is configured' do
- allow(Gitlab.config.topology_service).to receive(:enabled).and_return(true)
- expect(Gitlab.config.cell).to receive(:name).once.and_return(nil)
-
- expect { base_service }.to raise_error(NotImplementedError)
- end
end
end
end
diff --git a/spec/lib/gitlab/topology_service_client/cell_service_spec.rb b/spec/lib/gitlab/topology_service_client/cell_service_spec.rb
index d07a8771963..e1b9cc9b897 100644
--- a/spec/lib/gitlab/topology_service_client/cell_service_spec.rb
+++ b/spec/lib/gitlab/topology_service_client/cell_service_spec.rb
@@ -22,13 +22,6 @@ RSpec.describe Gitlab::TopologyServiceClient::CellService, feature_category: :ce
expect { cell_service }.to raise_error(NotImplementedError)
end
-
- it 'raises an error when no cell is configured' do
- allow(Gitlab.config.topology_service).to receive(:enabled).and_return(true)
- expect(Gitlab.config.cell).to receive(:name).once.and_return(nil)
-
- expect { cell_service }.to raise_error(NotImplementedError)
- end
end
context 'when topology service is enabled' do
diff --git a/spec/migrations/cap_workspaces_max_termination_to_one_year_spec.rb b/spec/migrations/cap_workspaces_max_termination_to_one_year_spec.rb
index 57db4ab1188..52c80b41132 100644
--- a/spec/migrations/cap_workspaces_max_termination_to_one_year_spec.rb
+++ b/spec/migrations/cap_workspaces_max_termination_to_one_year_spec.rb
@@ -45,7 +45,7 @@ RSpec.describe CapWorkspacesMaxTerminationToOneYear, feature_category: :workspac
desired_state: 'Terminated',
actual_state: 'Terminated',
editor: 'vs-code',
- devfile_ref: 'devfile-ref',
+ project_ref: 'devfile-ref',
devfile_path: 'devfile-path',
devfile: 'devfile',
processed_devfile: 'processed_dev_file',
@@ -68,7 +68,7 @@ RSpec.describe CapWorkspacesMaxTerminationToOneYear, feature_category: :workspac
desired_state: 'Running',
actual_state: 'Running',
editor: 'vs-code',
- devfile_ref: 'devfile-ref',
+ project_ref: 'devfile-ref',
devfile_path: 'devfile-path',
devfile: 'devfile',
processed_devfile: 'processed_dev_file',
diff --git a/spec/models/integrations/assembla_spec.rb b/spec/models/integrations/assembla_spec.rb
index 28cda0a1e75..d580b20bd47 100644
--- a/spec/models/integrations/assembla_spec.rb
+++ b/spec/models/integrations/assembla_spec.rb
@@ -3,49 +3,5 @@
require 'spec_helper'
RSpec.describe Integrations::Assembla, feature_category: :integrations do
- include StubRequests
-
- it_behaves_like Integrations::ResetSecretFields do
- let(:integration) { described_class.new }
- end
-
- describe 'Validations' do
- context 'when active' do
- before do
- subject.active = true
- end
-
- it { is_expected.to validate_presence_of :token }
- end
-
- context 'when inactive' do
- it { is_expected.not_to validate_presence_of :token }
- end
- end
-
- describe "#execute" do
- let_it_be(:user) { build(:user) }
- let_it_be(:project) { create(:project, :repository) }
-
- let(:assembla_integration) { described_class.new }
- let(:sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
- let(:api_url) { 'https://atlas.assembla.com/spaces/project_name/github_tool?secret_key=verySecret' }
-
- before do
- allow(assembla_integration).to receive_messages(
- project_id: project.id,
- project: project,
- token: 'verySecret',
- subdomain: 'project_name'
- )
- stub_full_request(api_url, method: :post)
- end
-
- it "calls Assembla API" do
- assembla_integration.execute(sample_data)
- expect(WebMock).to have_requested(:post, stubbed_hostname(api_url)).with(
- body: /#{sample_data[:before]}.*#{sample_data[:after]}.*#{project.path}/
- ).once
- end
- end
+ it_behaves_like Integrations::Base::Assembla
end
diff --git a/spec/models/integrations/instance/assembla_spec.rb b/spec/models/integrations/instance/assembla_spec.rb
new file mode 100644
index 00000000000..3d6a890d8b6
--- /dev/null
+++ b/spec/models/integrations/instance/assembla_spec.rb
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Integrations::Instance::Assembla, feature_category: :integrations do
+ it_behaves_like Integrations::Base::Assembla
+end
diff --git a/spec/models/network/graph_spec.rb b/spec/models/network/graph_spec.rb
index 61b46cbc867..78cf83cef91 100644
--- a/spec/models/network/graph_spec.rb
+++ b/spec/models/network/graph_spec.rb
@@ -52,46 +52,26 @@ RSpec.describe Network::Graph, feature_category: :source_code_management do
describe '#commits' do
let(:graph) { described_class.new(project, 'refs/heads/master', project.repository.commit, nil) }
- context 'when use_list_commits_rpc_network_graph FF is enabled' do
- let(:opts) do
- {
- revisions: %w[--tags --branches],
- pagination_params: { limit: 650 },
- reverse: false,
- order: :date,
- ref: 'refs/heads/master',
- skip: 0
- }
- end
-
- before do
- stub_feature_flags(use_list_commits_rpc_network_graph: true)
- end
-
- it 'only fetches the commits once using `list_all`', :request_store do
- expect(Gitlab::Git::Commit).to receive(:list_all)
- .with(project.repository.raw_repository, opts)
- .once
- .and_call_original
-
- graph
- end
-
- it_behaves_like 'a collection of commits'
+ let(:opts) do
+ {
+ revisions: %w[--tags --branches],
+ pagination_params: { limit: 650 },
+ reverse: false,
+ order: :date,
+ ref: 'refs/heads/master',
+ skip: 0
+ }
end
- context 'when use_list_commits_rpc_network_graph FF is disabled' do
- before do
- stub_feature_flags(use_list_commits_rpc_network_graph: false)
- end
+ it 'only fetches the commits once using `list_all`', :request_store do
+ expect(Gitlab::Git::Commit).to receive(:list_all)
+ .with(project.repository.raw_repository, opts)
+ .once
+ .and_call_original
- it 'only fetches the commits once using `find_all`', :request_store do
- expect(Gitlab::Git::Commit).to receive(:find_all).once.and_call_original
-
- graph
- end
-
- it_behaves_like 'a collection of commits'
+ graph
end
+
+ it_behaves_like 'a collection of commits'
end
end
diff --git a/spec/models/personal_access_token_spec.rb b/spec/models/personal_access_token_spec.rb
index 32079699e21..6df4acf083d 100644
--- a/spec/models/personal_access_token_spec.rb
+++ b/spec/models/personal_access_token_spec.rb
@@ -120,6 +120,25 @@ RSpec.describe PersonalAccessToken, feature_category: :system_access do
end
end
+ describe 'expires scopes', :time_freeze do
+ let!(:expires_last_month_token) { create(:personal_access_token, expires_at: 1.month.ago) }
+ let!(:expires_next_month_token) { create(:personal_access_token, expires_at: 1.month.from_now) }
+ let!(:expires_two_months_token) { create(:personal_access_token, expires_at: 2.months.from_now) }
+
+ describe '.expires_before' do
+ it 'finds tokens that expire before or on date' do
+ expect(described_class.expires_before(1.month.ago)).to contain_exactly(expires_last_month_token)
+ end
+ end
+
+ describe '.expires_after' do
+ it 'finds tokens that expires after or on date' do
+ expect(described_class.expires_after(1.month.from_now.beginning_of_hour))
+ .to contain_exactly(expires_next_month_token, expires_two_months_token)
+ end
+ end
+ end
+
describe '.last_used_before' do
context 'last_used_*' do
let_it_be(:date) { DateTime.new(2022, 01, 01) }
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index d7d5914f5ad..402b7a13dcd 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -3116,6 +3116,28 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
end
end
+ describe '#pages_unique_domain_enabled?' do
+ let(:project) { create(:project) }
+
+ subject { project.pages_unique_domain_enabled? }
+
+ context 'if unique domain is enabled' do
+ before do
+ project.project_setting.update!(pages_unique_domain_enabled: true, pages_unique_domain: 'foo123.example.com')
+ end
+
+ it { is_expected.to be(true) }
+ end
+
+ context 'if unique domain is disabled' do
+ before do
+ project.project_setting.update!(pages_unique_domain_enabled: false)
+ end
+
+ it { is_expected.to be(false) }
+ end
+ end
+
describe '#default_branch_protected?' do
let_it_be(:namespace) { create(:namespace) }
let_it_be(:project) { create(:project, namespace: namespace) }
@@ -9840,4 +9862,32 @@ RSpec.describe Project, factory_default: :keep, feature_category: :groups_and_pr
expect(project.uploads_sharding_key).to eq(namespace_id: namespace.id)
end
end
+
+ describe '#pages_domain_present?' do
+ let_it_be(:project) { create(:project) }
+
+ before do
+ allow(project).to receive(:pages_url).and_return('https://example.com')
+ end
+
+ context 'when the domain matches pages_url' do
+ it 'returns true' do
+ expect(project.pages_domain_present?('https://example.com')).to be(true)
+ end
+ end
+
+ context 'when the domain exists in pages_domains' do
+ let!(:pages_domain) { create(:pages_domain, project: project, domain: 'custom.com') }
+
+ it 'returns true' do
+ expect(project.pages_domain_present?('https://custom.com')).to be(true)
+ end
+ end
+
+ context 'when the domain does not match pages_url or pages_domains' do
+ it 'returns false' do
+ expect(project.pages_domain_present?('https://unknown.com')).to be(false)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/pages_spec.rb b/spec/requests/api/pages_spec.rb
index 8d23b64dc3c..5c7c997709b 100644
--- a/spec/requests/api/pages_spec.rb
+++ b/spec/requests/api/pages_spec.rb
@@ -110,7 +110,8 @@ RSpec.describe API::Pages, feature_category: :pages do
context 'and updates pages primary domain' do
let(:domain) { 'my.domain.com' }
- let(:params) { { pages_primary_domain: domain } }
+ let(:pages_primary_domain_url) { 'https://my.domain.com' }
+ let(:params) { { pages_primary_domain: pages_primary_domain_url } }
before do
create(:pages_domain, project: project, domain: domain)
@@ -120,7 +121,7 @@ RSpec.describe API::Pages, feature_category: :pages do
patch api(path, admin, admin_mode: true), params: params
expect(response).to have_gitlab_http_status(:ok)
- expect(json_response['pages_primary_domain']).to eq(domain)
+ expect(json_response['pages_primary_domain']).to eq(pages_primary_domain_url)
end
end
diff --git a/spec/support/shared_examples/models/concerns/integrations/base/assembla_shared_examples.rb b/spec/support/shared_examples/models/concerns/integrations/base/assembla_shared_examples.rb
new file mode 100644
index 00000000000..d5e15490933
--- /dev/null
+++ b/spec/support/shared_examples/models/concerns/integrations/base/assembla_shared_examples.rb
@@ -0,0 +1,49 @@
+# frozen_string_literal: true
+
+RSpec.shared_examples Integrations::Base::Assembla do
+ include StubRequests
+
+ it_behaves_like Integrations::ResetSecretFields do
+ let(:integration) { described_class.new }
+ end
+
+ describe 'Validations' do
+ context 'when active' do
+ before do
+ subject.active = true
+ end
+
+ it { is_expected.to validate_presence_of :token }
+ end
+
+ context 'when inactive' do
+ it { is_expected.not_to validate_presence_of :token }
+ end
+ end
+
+ describe "#execute" do
+ let_it_be(:user) { build(:user) }
+ let_it_be(:project) { create(:project, :repository) }
+
+ let(:assembla_integration) { described_class.new }
+ let(:sample_data) { Gitlab::DataBuilder::Push.build_sample(project, user) }
+ let(:api_url) { 'https://atlas.assembla.com/spaces/project_name/github_tool?secret_key=verySecret' }
+
+ it "calls Assembla API" do
+ allow(assembla_integration).to receive_messages(
+ project_id: project.id,
+ project: project,
+ token: 'verySecret',
+ subdomain: 'project_name'
+ )
+
+ stub_full_request(api_url, method: :post).with(body: { payload: sample_data })
+
+ assembla_integration.execute(sample_data)
+
+ expect(WebMock).to have_requested(:post, stubbed_hostname(api_url)).with(
+ body: /#{sample_data[:before]}.*#{sample_data[:after]}.*#{project.path}/
+ ).once
+ end
+ end
+end
diff --git a/spec/tasks/gitlab/db_rake_spec.rb b/spec/tasks/gitlab/db_rake_spec.rb
index af9e4ec2008..528ab1048a2 100644
--- a/spec/tasks/gitlab/db_rake_spec.rb
+++ b/spec/tasks/gitlab/db_rake_spec.rb
@@ -156,7 +156,6 @@ RSpec.describe 'gitlab:db namespace rake task', :silence_stdout, feature_categor
before do
allow(Settings).to receive(:topology_service_enabled?).and_return(topology_service_enabled)
- allow(Settings).to receive(:has_configured_cell?).and_return(configured_cell)
allow(Settings).to receive(:skip_sequence_alteration?).and_return(skip_sequence_alteration)
end
diff --git a/tooling/custom_roles/docs/templates/custom_abilities.md.erb b/tooling/custom_roles/docs/templates/custom_abilities.md.erb
index 6ed3de42a58..437638de096 100644
--- a/tooling/custom_roles/docs/templates/custom_abilities.md.erb
+++ b/tooling/custom_roles/docs/templates/custom_abilities.md.erb
@@ -18,6 +18,13 @@
<% return unless ability[:feature_flag] %>
<% "`#{ability[:feature_flag]}`" %>
<% end %>
+<% def scope(ability) %>
+<% scopes = [] %>
+<% scopes << 'Instance' if ability[:admin_ability]%>
+<% scopes << 'Group' if ability[:group_ability]%>
+<% scopes << 'Project' if ability[:project_ability]%>
+<% return scopes.join(',
') %>
+<% end %>
---
stage: Software Supply Chain Security
group: Authorization
@@ -46,9 +53,9 @@ Any dependencies are noted in the `Description` column for each permission.
## <%= "#{humanize(category)}" %>
-| Name | Description | Introduced | Feature flag | Enabled |
-|:-----|:------------|:-----------|:-------------|:--------|
+| Permission | Description | API Attribute | Scope | Introduced |
+|:-----------|:------------|:--------------|:------|:-----------|
<% abilities.each do |name, ability| %>
-| <%= "[`#{name}`](#{ability[:introduced_by_mr]})" %> | <%= ability[:description] %> | GitLab <%= "[#{ability[:milestone]}](#{ability[:introduced_by_issue]})" %> | <%= feature_flag(ability) %> | <%= enabled_link(ability) %> |
+| <%= ability[:title] %> | <%= ability[:description] %> | <%= "[`#{name}`](#{ability[:introduced_by_mr]})" %> | <%= scope(ability) %> | GitLab <%= "[#{ability[:milestone]}](#{ability[:introduced_by_issue]})" %> |
<% end %>
<% end %>