From 3822e951cb0ced8b24d79949339684312c8bb2e1 Mon Sep 17 00:00:00 2001
From: GitLab Bot
- + {{ $options.i18n.lockedText }}
@@ -112,8 +112,7 @@ export default {
+
path_to_directory }
%td.tree-item-file-name
= tree_icon('folder', '755', directory.name)
- = link_to path_to_directory, class: 'str-truncated', data: { qa_selector: 'directory_name_link', qa_directory_name: directory.name } do
+ = link_to path_to_directory, class: 'str-truncated', data: { testid: 'directory-name-link', qa_directory_name: directory.name } do
%span= directory.name
%td
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index 7b815d996e0..4a7aa9a86ab 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -45,8 +45,10 @@
.issuable-meta
%ul.controls.d-flex.align-items-end
- if merge_request.merged?
+ - merged_at = merge_request.merged_at ? l(merge_request.merged_at.to_time) : _("Merge date & time could not be determined")
%li.d-none.d-sm-flex
- = render Pajamas::BadgeComponent.new(_('Merged'), size: 'sm', variant: 'info')
+ %a.has-tooltip{ href: "#{merge_request_path(merge_request)}#widget-state", title: merged_at }
+ = render Pajamas::BadgeComponent.new(_('Merged'), size: 'sm', variant: 'info')
- elsif merge_request.closed?
%li.d-none.d-sm-flex
= render Pajamas::BadgeComponent.new(_('Closed'), size: 'sm', variant: 'danger')
diff --git a/app/views/registrations/welcome/show.html.haml b/app/views/registrations/welcome/show.html.haml
index caaa209a702..8fe29c815c9 100644
--- a/app/views/registrations/welcome/show.html.haml
+++ b/app/views/registrations/welcome/show.html.haml
@@ -33,7 +33,6 @@
= render_if_exists "registrations/welcome/jobs_to_be_done", f: f
= render_if_exists "registrations/welcome/setup_for_company", f: f
= render_if_exists "registrations/welcome/joining_project"
- = render 'devise/shared/email_opted_in', f: f
.row
.form-group.col-sm-12.gl-mb-0
- if partial_exists? "registrations/welcome/button"
diff --git a/doc/.vale/gitlab/spelling-exceptions.txt b/doc/.vale/gitlab/spelling-exceptions.txt
index 1e66d12adc9..361a080a2e4 100644
--- a/doc/.vale/gitlab/spelling-exceptions.txt
+++ b/doc/.vale/gitlab/spelling-exceptions.txt
@@ -109,6 +109,7 @@ bugfixed
bugfixes
bugfixing
Bugzilla
+Buildah
Buildkite
buildpack
buildpacks
@@ -644,6 +645,7 @@ OmniAuth
onboarding
OpenID
OpenShift
+OpenTelemetry
Opsgenie
Opstrace
ORMs
diff --git a/doc/administration/audit_event_streaming/audit_event_types.md b/doc/administration/audit_event_streaming/audit_event_types.md
index 331e991aeb3..cda14b41b5c 100644
--- a/doc/administration/audit_event_streaming/audit_event_types.md
+++ b/doc/administration/audit_event_streaming/audit_event_types.md
@@ -292,6 +292,7 @@ Every audit event is associated with an event type. The association with the eve
| [`user_enable_admin_mode`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/104754) | Event triggered on enabling admin mode | **{check-circle}** Yes | **{check-circle}** Yes | `system_access` | GitLab [15.7](https://gitlab.com/gitlab-org/gitlab/-/issues/362101) |
| [`user_impersonation`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79340) | Triggered when an instance administrator starts or stops impersonating a user | **{check-circle}** Yes | **{check-circle}** Yes | `user_management` | GitLab [14.8](https://gitlab.com/gitlab-org/gitlab/-/issues/300961) |
| [`user_password_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106086) | audit when user password is updated | **{check-circle}** Yes | **{check-circle}** Yes | `user_management` | GitLab [15.7](https://gitlab.com/gitlab-org/gitlab/-/issues/369330) |
+| [`user_profile_visiblity_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129149) | Triggered when user toggles private profile user setting | **{dotted-circle}** No | **{check-circle}** Yes | `user_profile` | GitLab [16.3](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/129149) |
| [`user_rejected`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113784) | Event triggered when a user registration is rejected | **{check-circle}** Yes | **{dotted-circle}** No | `user_management` | GitLab [15.11](https://gitlab.com/gitlab-org/gitlab/-/issues/374107) |
| [`user_username_updated`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106086) | Event triggered on updating a user's username | **{check-circle}** Yes | **{check-circle}** Yes | `user_profile` | GitLab [15.7](https://gitlab.com/gitlab-org/gitlab/-/issues/369329) |
| [`feature_flag_created`](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/113453) | Triggered when a feature flag is created. | **{check-circle}** Yes | **{check-circle}** Yes | `feature_flags` | GitLab [15.10](https://gitlab.com/gitlab-org/gitlab/-/issues/374109) |
diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md
index 3cba1ae9e6a..dd2693f4ba7 100644
--- a/doc/administration/geo/replication/troubleshooting.md
+++ b/doc/administration/geo/replication/troubleshooting.md
@@ -757,7 +757,7 @@ To solve this:
1. Sign in on the web interface for the secondary Geo site.
-1. Back up [the `.git` folder](../../repository_storage_types.md#translate-hashed-storage-paths).
+1. Back up [the `.git` folder](../../repository_storage_paths.md#translate-hashed-storage-paths).
1. Optional. [Spot-check](../../logs/log_parsing.md#find-all-projects-affected-by-a-fatal-git-problem)
a few of those IDs whether they indeed correspond
diff --git a/doc/administration/gitaly/index.md b/doc/administration/gitaly/index.md
index dc0b7a4a6c2..de2336d1cc1 100644
--- a/doc/administration/gitaly/index.md
+++ b/doc/administration/gitaly/index.md
@@ -326,7 +326,7 @@ conflicts that could occur due to partially applied operations.
Repositories are stored in the storages at the relative path determined by the [Gitaly client](#gitaly-architecture). These paths can be
identified by them not beginning with the `@cluster` prefix. The relative paths
-follow the [hashed storage](../repository_storage_types.md#hashed-storage) schema.
+follow the [hashed storage](../repository_storage_paths.md#hashed-storage) schema.
#### Praefect-generated replica paths (GitLab 15.0 and later)
@@ -377,7 +377,7 @@ Use the [`praefect metadata`](troubleshooting.md#view-repository-metadata) subco
The repository on disk also contains the project path in the Git configuration file. The configuration
file can be used to determine the project path even if the repository's metadata has been deleted.
-Follow the [instructions in hashed storage's documentation](../repository_storage_types.md#from-hashed-path-to-project-name).
+Follow the [instructions in hashed storage's documentation](../repository_storage_paths.md#from-hashed-path-to-project-name).
#### Atomicity of operations
diff --git a/doc/administration/gitaly/recovery.md b/doc/administration/gitaly/recovery.md
index 4f5f82aa571..45bde083a1a 100644
--- a/doc/administration/gitaly/recovery.md
+++ b/doc/administration/gitaly/recovery.md
@@ -372,7 +372,7 @@ sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.t
In this example, the virtual storage to specify is `default` or `storage-1`.
-- `-repository` is the repository's relative path in the storage [beginning with `@hashed`](../repository_storage_types.md#hashed-storage).
+- `-repository` is the repository's relative path in the storage [beginning with `@hashed`](../repository_storage_paths.md#hashed-storage).
For example:
```plaintext
@@ -463,14 +463,14 @@ sudo /opt/gitlab/embedded/bin/praefect -config /var/opt/gitlab/praefect/config.t
In this example, the virtual storage to specify is `default` or `storage-1`.
-- `-relative-path` is the relative path in the virtual storage. Usually [beginning with `@hashed`](../repository_storage_types.md#hashed-storage).
+- `-relative-path` is the relative path in the virtual storage. Usually [beginning with `@hashed`](../repository_storage_paths.md#hashed-storage).
For example:
```plaintext
@hashed/f5/ca/f5ca38f748a1d6eaf726b8a42fb575c3c71f1864a8143301782de13da2d9202b.git
```
-- `-replica-path` is the relative path on physical storage. Can start with [`@cluster` or match `relative_path`](../repository_storage_types.md#gitaly-cluster-storage).
+- `-replica-path` is the relative path on physical storage. Can start with [`@cluster` or match `relative_path`](../repository_storage_paths.md#gitaly-cluster-storage).
- `-authoritative-storage` is the storage we want Praefect to treat as the primary. Required if
[per-repository replication](praefect.md#configure-replication-factor) is set as the replication strategy.
- `-replicate-immediately`, available in GitLab 14.6 and later, causes the command to replicate the repository to its secondaries immediately.
diff --git a/doc/administration/raketasks/storage.md b/doc/administration/raketasks/storage.md
index db5f1ab52c5..ce931e78c2b 100644
--- a/doc/administration/raketasks/storage.md
+++ b/doc/administration/raketasks/storage.md
@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
This is a collection of Rake tasks to help you list and migrate
existing projects and their attachments to the new
-[hashed storage](../repository_storage_types.md) that GitLab
+[hashed storage](../repository_storage_paths.md) that GitLab
uses to organize the Git data.
## List projects and attachments
@@ -75,7 +75,7 @@ To have a summary and then a list of projects and their attachments using hashed
## Migrate to hashed storage
WARNING:
-In GitLab 13.0, [hashed storage](../repository_storage_types.md#hashed-storage)
+In GitLab 13.0, [hashed storage](../repository_storage_paths.md#hashed-storage)
is enabled by default and the legacy storage is deprecated.
GitLab 14.0 eliminates support for legacy storage. If you're on GitLab
13.0 and later, switching new projects to legacy storage is not possible.
@@ -129,7 +129,7 @@ You only need the `gitlab:storage:migrate_to_hashed` Rake task to migrate your r
## Rollback from hashed storage to legacy storage
WARNING:
-In GitLab 13.0, [hashed storage](../repository_storage_types.md#hashed-storage)
+In GitLab 13.0, [hashed storage](../repository_storage_paths.md#hashed-storage)
is enabled by default and the legacy storage is deprecated.
GitLab 14.0 eliminates support for legacy storage. If you're on GitLab
13.0 and later, switching new projects to legacy storage is not possible.
diff --git a/doc/administration/repository_checks.md b/doc/administration/repository_checks.md
index 55d0efd7e11..241f88793ff 100644
--- a/doc/administration/repository_checks.md
+++ b/doc/administration/repository_checks.md
@@ -65,7 +65,7 @@ You can run [`git fsck`](https://git-scm.com/docs/git-fsck) using the command li
by default.
- For GitLab Helm chart installations, repositories are stored in the `/home/git/repositories` directory inside the
Gitaly pod by default.
-1. [Identify the subdirectory that contains the repository](repository_storage_types.md#from-project-name-to-hashed-path)
+1. [Identify the subdirectory that contains the repository](repository_storage_paths.md#from-project-name-to-hashed-path)
that you need to check.
1. Run the check. For example:
diff --git a/doc/administration/repository_storage_paths.md b/doc/administration/repository_storage_paths.md
index c40c438216b..a3e0158fd24 100644
--- a/doc/administration/repository_storage_paths.md
+++ b/doc/administration/repository_storage_paths.md
@@ -22,6 +22,185 @@ For more information on:
- Configuring Gitaly, see [Configure Gitaly](gitaly/configure_gitaly.md).
- Configuring Gitaly Cluster, see [Configure Gitaly Cluster](gitaly/praefect.md).
+## Hashed storage
+
+> **Storage name** field [renamed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128416) from **Gitaly storage name** and **Relative path** field [renamed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128416) from **Gitaly relative path** in GitLab 16.3.
+
+Hashed storage stores projects on disk in a location based on a hash of the project's ID. This makes the folder
+structure immutable and eliminates the need to synchronize state from URLs to disk structure. This means that renaming a
+group, user, or project:
+
+- Costs only the database transaction.
+- Takes effect immediately.
+
+The hash also helps spread the repositories more evenly on the disk. The top-level directory
+contains fewer folders than the total number of top-level namespaces.
+
+The hash format is based on the hexadecimal representation of a SHA256, calculated with
+`SHA256(project.id)`. The top-level folder uses the first two characters, followed by another folder
+with the next two characters. They are both stored in a special `@hashed` folder so they can
+co-exist with existing legacy storage projects. For example:
+
+```ruby
+# Project's repository:
+"@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}.git"
+
+# Wiki's repository:
+"@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}.wiki.git"
+```
+
+### Translate hashed storage paths
+
+Troubleshooting problems with the Git repositories, adding hooks, and other tasks requires you
+translate between the human-readable project name and the hashed storage path. You can translate:
+
+- From a [project's name to its hashed path](#from-project-name-to-hashed-path).
+- From a [hashed path to a project's name](#from-hashed-path-to-project-name).
+
+#### From project name to hashed path
+
+Administrators can look up a project's hashed path from its name or ID using:
+
+- The [Admin Area](../administration/admin_area.md#administering-projects).
+- A Rails console.
+
+To look up a project's hash path in the Admin Area:
+
+1. On the left sidebar, select **Search or go to**.
+1. Select **Admin Area**.
+1. On the left sidebar, select **Overview > Projects** and select the project.
+1. Locate the **Relative path** field. The value is similar to:
+
+ ```plaintext
+ "@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git"
+ ```
+
+To look up a project's hash path using a Rails console:
+
+1. Start a [Rails console](operations/rails_console.md#starting-a-rails-console-session).
+1. Run a command similar to this example (use either the project's ID or its name):
+
+ ```ruby
+ Project.find(16).disk_path
+ Project.find_by_full_path('group/project').disk_path
+ ```
+
+#### From hashed path to project name
+
+Administrators can look up a project's name from its hashed relative path using:
+
+- A Rails console.
+- The `config` file in the `*.git` directory.
+
+To look up a project's name using the Rails console:
+
+1. Start a [Rails console](operations/rails_console.md#starting-a-rails-console-session).
+1. Run a command similar to this example:
+
+ ```ruby
+ ProjectRepository.find_by(disk_path: '@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9').project
+ ```
+
+The quoted string in that command is the directory tree you can find on your GitLab server. For
+example, on a default Linux package installation this would be `/var/opt/gitlab/git-data/repositories/@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git`
+with `.git` from the end of the directory name removed.
+
+The output includes the project ID and the project name. For example:
+
+```plaintext
+=> #
+```
+
+To look up a project's name using the `config` file in the `*.git` directory:
+
+1. Locate the `*.git` directory. This directory is located in `/var/opt/gitlab/git-data/repositories/@hashed/`, where the first four
+ characters of the hash are the first two directories in the path under `@hashed/`. For example, on a default Linux package installation the
+ `*.git` directory of the hash `b17eb17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9` would be
+ `/var/opt/gitlab/git-data/repositories/@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git`.
+1. Open the `config` file and locate the `fullpath=` key under `[gitlab]`.
+
+### Hashed object pools
+
+Object pools are repositories used to deduplicate forks of public and internal projects and
+contain the objects from the source project. Using `objects/info/alternates`, the source project and
+forks use the object pool for shared objects. For more information, see
+[How Git object deduplication works in GitLab](../development/git_object_deduplication.md).
+
+Objects are moved from the source project to the object pool when housekeeping is run on the source
+project. Object pool repositories are stored similarly to regular repositories in a directory called `@pools` instead of `@hashed`
+
+```ruby
+# object pool paths
+"@pools/#{hash[0..1]}/#{hash[2..3]}/#{hash}.git"
+```
+
+WARNING:
+Do not run `git prune` or `git gc` in object pool repositories, which are stored in the `@pools` directory.
+This can cause data loss in the regular repositories that depend on the object pool.
+
+### Group wiki storage
+
+Unlike project wikis that are stored in the `@hashed` directory, group wikis are stored in a directory called `@groups`.
+Like project wikis, group wikis follow the hashed storage folder convention, but use a hash of the group ID rather than the project ID.
+
+For example:
+
+```ruby
+# group wiki paths
+"@groups/#{hash[0..1]}/#{hash[2..3]}/#{hash}.wiki.git"
+```
+
+### Gitaly Cluster storage
+
+If Gitaly Cluster is used, Praefect manages storage locations. The internal path used by Praefect for the repository
+differs from the hashed path. For more information, see
+[Praefect-generated replica paths](gitaly/index.md#praefect-generated-replica-paths-gitlab-150-and-later).
+
+### Object storage support
+
+This table shows which storable objects are storable in each storage type:
+
+| Storable object | Hashed storage | S3 compatible |
+|:-----------------|:---------------|:--------------|
+| Repository | Yes | - |
+| Attachments | Yes | - |
+| Avatars | No | - |
+| Pages | No | - |
+| Docker Registry | No | - |
+| CI/CD job logs | No | - |
+| CI/CD artifacts | No | Yes |
+| CI/CD cache | No | Yes |
+| LFS objects | Similar | Yes |
+| Repository pools | Yes | - |
+
+Files stored in an S3-compatible endpoint can have the same advantages as
+[hashed storage](#hashed-storage), as long as they are not prefixed with
+`#{namespace}/#{project_name}`. This is true for CI/CD cache and LFS objects.
+
+#### Avatars
+
+Each file is stored in a directory that matches the `id` assigned to it in the database. The
+filename is always `avatar.png` for user avatars. When an avatar is replaced, the `Upload` model is
+destroyed and a new one takes place with a different `id`.
+
+#### CI/CD artifacts
+
+CI/CD artifacts are S3-compatible.
+
+#### LFS objects
+
+[LFS Objects in GitLab](../topics/git/lfs/index.md) implement a similar
+storage pattern using two characters and two-level folders, following the Git implementation:
+
+```ruby
+"shared/lfs-objects/#{oid[0..1}/#{oid[2..3]}/#{oid[4..-1]}"
+
+# Based on object `oid`: `8909029eb962194cfb326259411b22ae3f4a814b5be4f80651735aeef9f3229c`, path will be:
+"shared/lfs-objects/89/09/029eb962194cfb326259411b22ae3f4a814b5be4f80651735aeef9f3229c"
+```
+
+LFS objects are also [S3-compatible](lfs/index.md#storing-lfs-objects-in-remote-object-storage).
+
## Configure where new repositories are stored
After you configure multiple repository storages, you can choose where new repositories are stored:
diff --git a/doc/administration/repository_storage_types.md b/doc/administration/repository_storage_types.md
index 403d8f7b690..844dc76b23d 100644
--- a/doc/administration/repository_storage_types.md
+++ b/doc/administration/repository_storage_types.md
@@ -1,202 +1,8 @@
---
-stage: Systems
-group: Gitaly
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+redirect_to: 'repository_storage_paths.md'
+remove_date: '2023-11-29'
---
-# Repository storage types **(FREE SELF)**
+This document was moved to [another location](repository_storage_paths.md).
-GitLab can be configured to use one or multiple repository storages. These storages are accessed through either:
-
-- [Gitaly](gitaly/index.md).
-- [Gitaly Cluster](gitaly/index.md#gitaly-cluster) as virtual storage.
-
-In GitLab:
-
-- Repository storages are configured in:
- - `/etc/gitlab/gitlab.rb` by the `git_data_dirs({})` configuration hash for Linux package installations.
- - `gitlab.yml` by the `repositories.storages` key for self-compiled installations.
-- The `default` repository storage is available in any installations that haven't customized it. By
- default, it points to a Gitaly node.
-
-The repository storage types documented here apply to any repository storage defined in
-`git_data_dirs({})` or `repositories.storages`.
-
-## Hashed storage
-
-> **Storage name** field [renamed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128416) from **Gitaly storage name** and **Relative path** field [renamed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/128416) from **Gitaly relative path** in GitLab 16.3.
-
-Hashed storage stores projects on disk in a location based on a hash of the project's ID. This makes the folder
-structure immutable and eliminates the need to synchronize state from URLs to disk structure. This means that renaming a
-group, user, or project:
-
-- Costs only the database transaction.
-- Takes effect immediately.
-
-The hash also helps spread the repositories more evenly on the disk. The top-level directory
-contains fewer folders than the total number of top-level namespaces.
-
-The hash format is based on the hexadecimal representation of a SHA256, calculated with
-`SHA256(project.id)`. The top-level folder uses the first two characters, followed by another folder
-with the next two characters. They are both stored in a special `@hashed` folder so they can
-co-exist with existing legacy storage projects. For example:
-
-```ruby
-# Project's repository:
-"@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}.git"
-
-# Wiki's repository:
-"@hashed/#{hash[0..1]}/#{hash[2..3]}/#{hash}.wiki.git"
-```
-
-### Translate hashed storage paths
-
-Troubleshooting problems with the Git repositories, adding hooks, and other tasks requires you
-translate between the human-readable project name and the hashed storage path. You can translate:
-
-- From a [project's name to its hashed path](#from-project-name-to-hashed-path).
-- From a [hashed path to a project's name](#from-hashed-path-to-project-name).
-
-#### From project name to hashed path
-
-Administrators can look up a project's hashed path from its name or ID using:
-
-- The [Admin Area](../administration/admin_area.md#administering-projects).
-- A Rails console.
-
-To look up a project's hash path in the Admin Area:
-
-1. On the left sidebar, select **Search or go to**.
-1. Select **Admin Area**.
-1. On the left sidebar, select **Overview > Projects** and select the project.
-1. Locate the **Relative path** field. The value is similar to:
-
- ```plaintext
- "@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git"
- ```
-
-To look up a project's hash path using a Rails console:
-
-1. Start a [Rails console](operations/rails_console.md#starting-a-rails-console-session).
-1. Run a command similar to this example (use either the project's ID or its name):
-
- ```ruby
- Project.find(16).disk_path
- Project.find_by_full_path('group/project').disk_path
- ```
-
-#### From hashed path to project name
-
-Administrators can look up a project's name from its hashed relative path using:
-
-- A Rails console.
-- The `config` file in the `*.git` directory.
-
-To look up a project's name using the Rails console:
-
-1. Start a [Rails console](operations/rails_console.md#starting-a-rails-console-session).
-1. Run a command similar to this example:
-
- ```ruby
- ProjectRepository.find_by(disk_path: '@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9').project
- ```
-
-The quoted string in that command is the directory tree you can find on your GitLab server. For
-example, on a default Linux package installation this would be `/var/opt/gitlab/git-data/repositories/@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git`
-with `.git` from the end of the directory name removed.
-
-The output includes the project ID and the project name. For example:
-
-```plaintext
-=> #
-```
-
-To look up a project's name using the `config` file in the `*.git` directory:
-
-1. Locate the `*.git` directory. This directory is located in `/var/opt/gitlab/git-data/repositories/@hashed/`, where the first four
- characters of the hash are the first two directories in the path under `@hashed/`. For example, on a default Linux package installation the
- `*.git` directory of the hash `b17eb17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9` would be
- `/var/opt/gitlab/git-data/repositories/@hashed/b1/7e/b17ef6d19c7a5b1ee83b907c595526dcb1eb06db8227d650d5dda0a9f4ce8cd9.git`.
-1. Open the `config` file and locate the `fullpath=` key under `[gitlab]`.
-
-### Hashed object pools
-
-Object pools are repositories used to deduplicate forks of public and internal projects and
-contain the objects from the source project. Using `objects/info/alternates`, the source project and
-forks use the object pool for shared objects. For more information, see
-[How Git object deduplication works in GitLab](../development/git_object_deduplication.md).
-
-Objects are moved from the source project to the object pool when housekeeping is run on the source
-project. Object pool repositories are stored similarly to regular repositories in a directory called `@pools` instead of `@hashed`
-
-```ruby
-# object pool paths
-"@pools/#{hash[0..1]}/#{hash[2..3]}/#{hash}.git"
-```
-
-WARNING:
-Do not run `git prune` or `git gc` in object pool repositories, which are stored in the `@pools` directory.
-This can cause data loss in the regular repositories that depend on the object pool.
-
-### Group wiki storage
-
-Unlike project wikis that are stored in the `@hashed` directory, group wikis are stored in a directory called `@groups`.
-Like project wikis, group wikis follow the hashed storage folder convention, but use a hash of the group ID rather than the project ID.
-
-For example:
-
-```ruby
-# group wiki paths
-"@groups/#{hash[0..1]}/#{hash[2..3]}/#{hash}.wiki.git"
-```
-
-### Gitaly Cluster storage
-
-If Gitaly Cluster is used, Praefect manages storage locations. The internal path used by Praefect for the repository
-differs from the hashed path. For more information, see
-[Praefect-generated replica paths](gitaly/index.md#praefect-generated-replica-paths-gitlab-150-and-later).
-
-### Object storage support
-
-This table shows which storable objects are storable in each storage type:
-
-| Storable object | Hashed storage | S3 compatible |
-|:-----------------|:---------------|:--------------|
-| Repository | Yes | - |
-| Attachments | Yes | - |
-| Avatars | No | - |
-| Pages | No | - |
-| Docker Registry | No | - |
-| CI/CD job logs | No | - |
-| CI/CD artifacts | No | Yes |
-| CI/CD cache | No | Yes |
-| LFS objects | Similar | Yes |
-| Repository pools | Yes | - |
-
-Files stored in an S3-compatible endpoint can have the same advantages as
-[hashed storage](#hashed-storage), as long as they are not prefixed with
-`#{namespace}/#{project_name}`. This is true for CI/CD cache and LFS objects.
-
-#### Avatars
-
-Each file is stored in a directory that matches the `id` assigned to it in the database. The
-filename is always `avatar.png` for user avatars. When an avatar is replaced, the `Upload` model is
-destroyed and a new one takes place with a different `id`.
-
-#### CI/CD artifacts
-
-CI/CD artifacts are S3-compatible.
-
-#### LFS objects
-
-[LFS Objects in GitLab](../topics/git/lfs/index.md) implement a similar
-storage pattern using two characters and two-level folders, following the Git implementation:
-
-```ruby
-"shared/lfs-objects/#{oid[0..1}/#{oid[2..3]}/#{oid[4..-1]}"
-
-# Based on object `oid`: `8909029eb962194cfb326259411b22ae3f4a814b5be4f80651735aeef9f3229c`, path will be:
-"shared/lfs-objects/89/09/029eb962194cfb326259411b22ae3f4a814b5be4f80651735aeef9f3229c"
-```
-
-LFS objects are also [S3-compatible](lfs/index.md#storing-lfs-objects-in-remote-object-storage).
+
diff --git a/doc/administration/server_hooks.md b/doc/administration/server_hooks.md
index 75ee3eda9bb..4d8377b4117 100644
--- a/doc/administration/server_hooks.md
+++ b/doc/administration/server_hooks.md
@@ -39,7 +39,7 @@ Prerequisites:
- The [storage name](gitaly/configure_gitaly.md#gitlab-requires-a-default-repository-storage), path to the Gitaly configuration file
(default is `/var/opt/gitlab/gitaly/config.toml` on Linux package instances), and the
- [repository relative path](repository_storage_types.md#from-project-name-to-hashed-path) for the repository.
+ [repository relative path](repository_storage_paths.md#from-project-name-to-hashed-path) for the repository.
To set server hooks for a repository:
@@ -76,10 +76,10 @@ To create server hooks for a repository:
1. Go to **Overview > Projects** and select the project you want to add a server hook to.
1. On the page that appears, locate the value of **Relative path**. This path is where server
hooks must be located.
- - If you are using [hashed storage](repository_storage_types.md#hashed-storage), see
- [Translate hashed storage paths](repository_storage_types.md#translate-hashed-storage-paths) for information on
+ - If you are using [hashed storage](repository_storage_paths.md#hashed-storage), see
+ [Translate hashed storage paths](repository_storage_paths.md#translate-hashed-storage-paths) for information on
interpreting the relative path.
- - If you are not using [hashed storage](repository_storage_types.md#hashed-storage):
+ - If you are not using [hashed storage](repository_storage_paths.md#hashed-storage):
- For Linux package installations, the path is usually `/var/opt/gitlab/git-data/repositories//.git`.
- For self-compiled installations, the path is usually `/home/git/repositories//.git`.
1. On the file system, create a new directory in the correct location called `custom_hooks`.
@@ -109,7 +109,7 @@ To accomplish this, follow the same steps for setting custom repository hooks fo
The location to copy the scripts to depends on where repositories are stored:
-- In GitLab 15.2 and earlier, Gitaly Cluster uses the [hashed storage path](repository_storage_types.md#hashed-storage)
+- In GitLab 15.2 and earlier, Gitaly Cluster uses the [hashed storage path](repository_storage_paths.md#hashed-storage)
reported by the GitLab application.
- In GitLab 15.3 and later, new repositories are created using
[Praefect-generated replica paths](gitaly/index.md#praefect-generated-replica-paths-gitlab-150-and-later),
@@ -169,7 +169,7 @@ subdirectories.
Prerequisites:
-- The [storage name and relative path](repository_storage_types.md#from-project-name-to-hashed-path) for the repository.
+- The [storage name and relative path](repository_storage_paths.md#from-project-name-to-hashed-path) for the repository.
To remove server hooks, pass an empty tarball to `hook set` to indicate that the repository should contain no hooks. For example:
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index ffeeae09611..fe88e2e4129 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -12589,11 +12589,22 @@ Duo Chat message.
| `content` | [`String`](#string) | Content of the message. Can be null for failed responses. |
| `contentHtml` | [`String`](#string) | Content of the message in HTML format. Can be null for failed responses. |
| `errors` | [`[String!]!`](#string) | Errors that occurred while asynchronously fetching an AI (assistant) response. |
+| `extras` | [`AiMessageExtras`](#aimessageextras) | Extra message metadata. |
| `id` | [`ID`](#id) | UUID of the message. |
| `requestId` | [`ID`](#id) | UUID of the original request message. Shared between chat prompt and response. |
| `role` | [`AiChatMessageRole!`](#aichatmessagerole) | Message role. |
| `timestamp` | [`Time!`](#time) | Message timestamp. |
+### `AiMessageExtras`
+
+Extra metadata for AI message.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| `sources` | [`[JSON!]`](#json) | Sources used to form the message. |
+
### `AiMessageType`
#### Fields
@@ -12614,6 +12625,7 @@ Duo Chat message.
| ---- | ---- | ----------- |
| `chunkId` | [`Int`](#int) | Incremental ID for a chunk from a streamed response. Null when it is not a streamed response. |
| `errors` | [`[String!]`](#string) | Errors return by AI API as response. |
+| `extras` | [`AiMessageExtras`](#aimessageextras) | Extra message metadata. |
| `requestId` | [`String`](#string) | ID of the original request. |
| `responseBody` | [`String`](#string) | Response body from AI API. |
| `responseBodyHtml` | [`String`](#string) | Response body HTML. |
diff --git a/doc/ci/docker/buildah_rootless_tutorial.md b/doc/ci/docker/buildah_rootless_tutorial.md
new file mode 100644
index 00000000000..fdb33fd4a8b
--- /dev/null
+++ b/doc/ci/docker/buildah_rootless_tutorial.md
@@ -0,0 +1,149 @@
+---
+stage: Verify
+group: Pipeline Execution
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+type: howto
+---
+
+# Tutorial: Use Buildah in a rootless container with GitLab Runner Operator on OpenShift **(FREE)**
+
+This tutorial teaches you how to successfully build images using the `buildah` tool,
+with GitLab Runner deployed using [GitLab Runner Operator](https://gitlab.com/gitlab-org/gl-openshift/gitlab-runner-operator)
+on an OpenShift cluster.
+
+This guide is an adaptation of [using Buildah to build images in a rootless OpenShift container](https://github.com/containers/buildah/blob/main/docs/tutorials/05-openshift-rootless-build.md)
+documentation for GitLab Runner Operator.
+
+To complete this tutorial, you will:
+
+1. [Configure the Buildah image](#configure-the-buildah-image)
+1. [Configure the service account](#configure-the-service-account)
+1. [Configure the job](#configure-the-job)
+
+## Prerequisites
+
+- A runner already deployed to a `gitlab-runner` namespace.
+
+## Configure the Buildah image
+
+We start by preparing a custom image based on the `quay.io/buildah/stable:v1.23.1` image.
+
+1. Create the `Containerfile-buildah` file:
+
+ ```shell
+ cat > Containerfile-buildah < /etc/subuid \
+ && echo build:10000:65536 > /etc/subgid
+
+ # Use chroot since the default runc does not work when running rootless
+ RUN echo "export BUILDAH_ISOLATION=chroot" >> /home/build/.bashrc
+
+ # Use VFS since fuse does not work
+ RUN mkdir -p /home/build/.config/containers \
+ && (echo '[storage]';echo 'driver = "vfs"') > /home/build/.config/containers/storage.conf
+
+ # The buildah container will run as `build` user
+ USER build
+ WORKDIR /home/build
+ EOF
+ ```
+
+1. Build and push the Buildah image to a Container Registry. Let's push to the
+ [GitLab Container Registry](../../user/packages/container_registry/index.md):
+
+ ```shell
+ docker build -f Containerfile-buildah -t registry.example.com/group/project/buildah:1.23.1 .
+ docker push registry.example.com/group/project/buildah:1.23.1
+ ```
+
+## Configure the service account
+
+For these steps, you need to run the commands in a terminal connected to the OpenShift cluster.
+
+1. Run this command to create a service account named `buildah-sa`:
+
+ ```shell
+ oc create -f - < {
+ allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/422405")
+ }, through: :group_members
+end
+```
+
+WARNING:
+Overriding an association can have unintended consequences and may even lead to data loss, as we noticed in [issue 424307](https://gitlab.com/gitlab-org/gitlab/-/issues/424307). Do not override existing ActiveRecord associations to mark a cross-join as allowed, as in the example below.
+
+```ruby
+class Group < Namespace
+ has_many :users, through: :group_members
+
+ # DO NOT override an association like this.
+ def users
+ super.allow_cross_joins_across_databases(url: "https://gitlab.com/gitlab-org/gitlab/-/issues/422405")
+ end
+end
+```
+
The `url` parameter should point to an issue with a milestone for when we intend
to fix the cross-join. If the cross-join is being used in a migration, we do not
need to fix the code. See
diff --git a/doc/development/fe_guide/architecture.md b/doc/development/fe_guide/architecture.md
index 0c85a21fdf4..810d9af2de7 100644
--- a/doc/development/fe_guide/architecture.md
+++ b/doc/development/fe_guide/architecture.md
@@ -6,16 +6,11 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Architecture
-When developing a feature that requires architectural design, or changing the fundamental design of an existing feature, discuss it with a Frontend Architecture Expert.
+When building new features, consider reaching out to relevant stakeholders as early as possible in the process.
-A Frontend Architect is an expert who makes high-level Frontend design decisions
-and decides on technical standards, including coding standards and frameworks.
-
-Architectural decisions should be accessible to everyone, so document
-them in the relevant Merge Request discussion or by updating our documentation
-when appropriate.
-
-You can find the Frontend Architecture experts on the [team page](https://about.gitlab.com/company/team/).
+Architectural decisions should be accessible to everyone. Document
+them in the relevant Merge Request discussions or by updating our documentation
+when appropriate by adding an entry to this section.
## Widget Architecture
@@ -23,8 +18,3 @@ The [Plan stage](https://about.gitlab.com/handbook/engineering/development/dev/p
is refactoring the right sidebar to consist of **widgets**. They have a specific architecture to be
reusable and to expose an interface that can be used by external Vue applications on the page.
Learn more about the [widget architecture](widgets.md).
-
-## Examples
-
-You can find [documentation about the desired architecture](vue.md) for a new
-feature built with Vue.js.
diff --git a/doc/development/fe_guide/axios.md b/doc/development/fe_guide/axios.md
index f90a2b37a1c..876855b807c 100644
--- a/doc/development/fe_guide/axios.md
+++ b/doc/development/fe_guide/axios.md
@@ -6,9 +6,9 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Axios
-We use [Axios](https://github.com/axios/axios) to communicate with the server in Vue applications and most new code.
+In older parts of our codebase using the REST API, we used [Axios](https://github.com/axios/axios) to communicate with the server, but you should not use Axios in new applications. Instead rely on `apollo-client` to query the GraphQL API. For more details, see [our GraphQL documentation](graphql.md).
-In order to guarantee all defaults are set you *should not use Axios directly*, you should import Axios from `axios_utils`.
+To guarantee all defaults are set you should import Axios from `axios_utils`. Do not use Axios directly.
## CSRF token
diff --git a/doc/development/fe_guide/design_anti_patterns.md b/doc/development/fe_guide/design_anti_patterns.md
index f087fbd8235..e0d3a80d9c8 100644
--- a/doc/development/fe_guide/design_anti_patterns.md
+++ b/doc/development/fe_guide/design_anti_patterns.md
@@ -1,218 +1,10 @@
---
-stage: none
-group: unassigned
-info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+redirect_to: 'design_patterns.md'
+remove_date: '2023-12-07'
---
-# Design Anti-patterns
+This document was moved to [another location](design_patterns.md).
-Anti-patterns may seem like good approaches at first, but it has been shown that they bring more ills than benefits. These should
-generally be avoided.
-
-Throughout the GitLab codebase, there may be historic uses of these anti-patterns. [Use discretion](https://about.gitlab.com/handbook/engineering/development/principles/#balance-refactoring-and-velocity)
-when figuring out whether or not to refactor, when touching code that uses one of these legacy patterns.
-
-NOTE:
-For new features, anti-patterns are not necessarily prohibited, but it is **strongly suggested** to find another approach.
-
-## Shared Global Object (Anti-pattern)
-
-A shared global object is an instance of something that can be accessed from anywhere and therefore has no clear owner.
-
-Here's an example of this pattern applied to a Vuex Store:
-
-```javascript
-const createStore = () => new Vuex.Store({
- actions,
- state,
- mutations
-});
-
-// Notice that we are forcing all references to this module to use the same single instance of the store.
-// We are also creating the store at import-time and there is nothing which can automatically dispose of it.
-//
-// As an alternative, we should export the `createStore` and let the client manage the
-// lifecycle and instance of the store.
-export default createStore();
-```
-
-### What problems do Shared Global Objects cause?
-
-Shared Global Objects are convenient because they can be accessed from anywhere. However,
-the convenience does not always outweigh their heavy cost:
-
-- **No ownership.** There is no clear owner to these objects and therefore they assume a non-deterministic
- and permanent lifecycle. This can be especially problematic for tests.
-- **No access control.** When Shared Global Objects manage some state, this can create some very buggy and difficult
- coupling situations because there is no access control to this object.
-- **Possible circular references.** Shared Global Objects can also create some circular referencing situations since submodules
- of the Shared Global Object can reference modules that reference itself (see
- [this MR for an example](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33366)).
-
-Here are some historic examples where this pattern was identified to be problematic:
-
-- [Reference to global Vuex store in IDE](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36401)
-- [Docs update to discourage singleton Vuex store](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36952)
-
-### When could the Shared Global Object pattern be actually appropriate?
-
-Shared Global Object's solve the problem of making something globally accessible. This pattern
-could be appropriate:
-
-- When a responsibility is truly global and should be referenced across the application
- (for example, an application-wide Event Bus).
-
-Even in these scenarios, consider avoiding the Shared Global Object pattern because the
-side-effects can be notoriously difficult to reason with.
-
-### References
-
-For more information, see [Global Variables Are Bad on the C2 wiki](https://wiki.c2.com/?GlobalVariablesAreBad).
-
-## Singleton (Anti-pattern)
-
-The classic [Singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern) is an approach to ensure that only one
-instance of a thing exists.
-
-Here's an example of this pattern:
-
-```javascript
-class MyThing {
- constructor() {
- // ...
- }
-
- // ...
-}
-
-MyThing.instance = null;
-
-export const getThingInstance = () => {
- if (MyThing.instance) {
- return MyThing.instance;
- }
-
- const instance = new MyThing();
- MyThing.instance = instance;
- return instance;
-};
-```
-
-### What problems do Singletons cause?
-
-It is a big assumption that only one instance of a thing should exist. More often than not,
-a Singleton is misused and causes very tight coupling amongst itself and the modules that reference it.
-
-Here are some historic examples where this pattern was identified to be problematic:
-
-- [Test issues caused by singleton class in IDE](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30398#note_331174190)
-- [Implicit Singleton created by module's shared variables](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/merge_requests/97#note_417515776)
-- [Complexity caused by Singletons](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29461#note_324585814)
-
-Here are some ills that Singletons often produce:
-
-1. **Non-deterministic tests.** Singletons encourage non-deterministic tests because the single instance is shared across
- individual tests, often causing the state of one test to bleed into another.
-1. **High coupling.** Under the hood, clients of a singleton class all share a single specific
- instance of an object, which means this pattern inherits all the [problems of Shared Global Object](#what-problems-do-shared-global-objects-cause)
- such as no clear ownership and no access control. These leads to high coupling situations that can
- be buggy and difficult to untangle.
-1. **Infectious.** Singletons are infectious, especially when they manage state. Consider the component
- [RepoEditor](https://gitlab.com/gitlab-org/gitlab/-/blob/27ad6cb7b76430fbcbaf850df68c338d6719ed2b/app%2Fassets%2Fjavascripts%2Fide%2Fcomponents%2Frepo_editor.vue#L0-1)
- used in the Web IDE. This component interfaces with a Singleton [Editor](https://gitlab.com/gitlab-org/gitlab/-/blob/862ad57c44ec758ef3942ac2e7a2bd40a37a9c59/app%2Fassets%2Fjavascripts%2Fide%2Flib%2Feditor.js#L21)
- which manages some state for working with Monaco. Because of the Singleton nature of the Editor class,
- the component `RepoEditor` is now forced to be a Singleton as well. Multiple instances of this component
- would cause production issues because no one truly owns the instance of `Editor`.
-
-### Why is the Singleton pattern popular in other languages like Java?
-
-This is because of the limitations of languages like Java where everything has to be wrapped
-in a class. In JavaScript we have things like object and function literals where we can solve
-many problems with a module that exports utility functions.
-
-### When could the Singleton pattern be actually appropriate?**
-
-Singletons solve the problem of enforcing there to be only 1 instance of a thing. It's possible
-that a Singleton could be appropriate in the following rare cases:
-
-- We need to manage some resource that **MUST** have just 1 instance (that is, some hardware restriction).
-- There is a real [cross-cutting concern](https://en.wikipedia.org/wiki/Cross-cutting_concern) (for example, logging) and a Singleton provides the simplest API.
-
-Even in these scenarios, consider avoiding the Singleton pattern.
-
-### What alternatives are there to the Singleton pattern?
-
-#### Utility Functions
-
-When no state needs to be managed, we can export utility functions from a module without
-messing with any class instantiation.
-
-```javascript
-// bad - Singleton
-export class ThingUtils {
- static create() {
- if(this.instance) {
- return this.instance;
- }
-
- this.instance = new ThingUtils();
- return this.instance;
- }
-
- bar() { /* ... */ }
-
- fuzzify(id) { /* ... */ }
-}
-
-// good - Utility functions
-export const bar = () => { /* ... */ };
-
-export const fuzzify = (id) => { /* ... */ };
-```
-
-#### Dependency Injection
-
-[Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) is an approach which breaks
-coupling by declaring a module's dependencies to be injected from outside the module (for example, through constructor parameters, a bona-fide Dependency Injection framework, and even in Vue `provide/inject`).
-
-```javascript
-// bad - Vue component coupled to Singleton
-export default {
- created() {
- this.mediator = MyFooMediator.getInstance();
- },
-};
-
-// good - Vue component declares dependency
-export default {
- inject: ['mediator']
-};
-```
-
-```javascript
-// bad - We're not sure where the singleton is in it's lifecycle so we init it here.
-export class Foo {
- constructor() {
- Bar.getInstance().init();
- }
-
- stuff() {
- return Bar.getInstance().doStuff();
- }
-}
-
-// good - Lets receive this dependency as a constructor argument.
-// It's also not our responsibility to manage the lifecycle.
-export class Foo {
- constructor(bar) {
- this.bar = bar;
- }
-
- stuff() {
- return this.bar.doStuff();
- }
-}
-```
-
-In this example, the lifecycle and implementation details of `mediator` are all managed
-**outside** the component (most likely the page entrypoint).
+
+
+
diff --git a/doc/development/fe_guide/design_patterns.md b/doc/development/fe_guide/design_patterns.md
index 3c273ab18e9..44238ff5dc5 100644
--- a/doc/development/fe_guide/design_patterns.md
+++ b/doc/development/fe_guide/design_patterns.md
@@ -6,12 +6,226 @@ info: To determine the technical writer assigned to the Stage/Group associated w
# Design Patterns
-The following design patterns are suggested approaches for solving common problems. Use discretion when evaluating
-if a certain pattern makes sense in your situation. Just because it is a pattern, doesn't mean it is a good one for your problem.
+This page covers suggested design patterns and also anti-patterns.
NOTE:
When adding a design pattern to this document, be sure to clearly state the **problem it solves**.
+When adding a design anti-pattern, clearly state **the problem it prevents**.
-## TBD
+## Patterns
-Stay tuned!
+The following design patterns are suggested approaches for solving common problems. Use discretion when evaluating
+if a certain pattern makes sense in your situation. Just because it is a pattern, doesn't mean it is a good one for your problem.
+
+## Anti-patterns
+
+Anti-patterns may seem like good approaches at first, but it has been shown that they bring more ills than benefits. These should
+generally be avoided.
+
+Throughout the GitLab codebase, there may be historic uses of these anti-patterns. [Use discretion](https://about.gitlab.com/handbook/engineering/development/principles/#balance-refactoring-and-velocity)
+when figuring out whether or not to refactor, when touching code that uses one of these legacy patterns.
+
+NOTE:
+For new features, anti-patterns are not necessarily prohibited, but it is **strongly suggested** to find another approach.
+
+### Shared Global Object
+
+A shared global object is an instance of something that can be accessed from anywhere and therefore has no clear owner.
+
+Here's an example of this pattern applied to a Vuex Store:
+
+```javascript
+const createStore = () => new Vuex.Store({
+ actions,
+ state,
+ mutations
+});
+
+// Notice that we are forcing all references to this module to use the same single instance of the store.
+// We are also creating the store at import-time and there is nothing which can automatically dispose of it.
+//
+// As an alternative, we should export the `createStore` and let the client manage the
+// lifecycle and instance of the store.
+export default createStore();
+```
+
+#### What problems do Shared Global Objects cause?
+
+Shared Global Objects are convenient because they can be accessed from anywhere. However,
+the convenience does not always outweigh their heavy cost:
+
+- **No ownership.** There is no clear owner to these objects and therefore they assume a non-deterministic
+ and permanent lifecycle. This can be especially problematic for tests.
+- **No access control.** When Shared Global Objects manage some state, this can create some very buggy and difficult
+ coupling situations because there is no access control to this object.
+- **Possible circular references.** Shared Global Objects can also create some circular referencing situations since submodules
+ of the Shared Global Object can reference modules that reference itself (see
+ [this MR for an example](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/33366)).
+
+Here are some historic examples where this pattern was identified to be problematic:
+
+- [Reference to global Vuex store in IDE](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36401)
+- [Docs update to discourage singleton Vuex store](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36952)
+
+#### When could the Shared Global Object pattern be actually appropriate?
+
+Shared Global Object's solve the problem of making something globally accessible. This pattern
+could be appropriate:
+
+- When a responsibility is truly global and should be referenced across the application
+ (for example, an application-wide Event Bus).
+
+Even in these scenarios, consider avoiding the Shared Global Object pattern because the
+side-effects can be notoriously difficult to reason with.
+
+#### References
+
+For more information, see [Global Variables Are Bad on the C2 wiki](https://wiki.c2.com/?GlobalVariablesAreBad).
+
+### Singleton
+
+The classic [Singleton pattern](https://en.wikipedia.org/wiki/Singleton_pattern) is an approach to ensure that only one
+instance of a thing exists.
+
+Here's an example of this pattern:
+
+```javascript
+class MyThing {
+ constructor() {
+ // ...
+ }
+
+ // ...
+}
+
+MyThing.instance = null;
+
+export const getThingInstance = () => {
+ if (MyThing.instance) {
+ return MyThing.instance;
+ }
+
+ const instance = new MyThing();
+ MyThing.instance = instance;
+ return instance;
+};
+```
+
+#### What problems do Singletons cause?
+
+It is a big assumption that only one instance of a thing should exist. More often than not,
+a Singleton is misused and causes very tight coupling amongst itself and the modules that reference it.
+
+Here are some historic examples where this pattern was identified to be problematic:
+
+- [Test issues caused by singleton class in IDE](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30398#note_331174190)
+- [Implicit Singleton created by module's shared variables](https://gitlab.com/gitlab-org/gitlab-vscode-extension/-/merge_requests/97#note_417515776)
+- [Complexity caused by Singletons](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29461#note_324585814)
+
+Here are some ills that Singletons often produce:
+
+1. **Non-deterministic tests.** Singletons encourage non-deterministic tests because the single instance is shared across
+ individual tests, often causing the state of one test to bleed into another.
+1. **High coupling.** Under the hood, clients of a singleton class all share a single specific
+ instance of an object, which means this pattern inherits all the [problems of Shared Global Object](#what-problems-do-shared-global-objects-cause)
+ such as no clear ownership and no access control. These leads to high coupling situations that can
+ be buggy and difficult to untangle.
+1. **Infectious.** Singletons are infectious, especially when they manage state. Consider the component
+ [RepoEditor](https://gitlab.com/gitlab-org/gitlab/-/blob/27ad6cb7b76430fbcbaf850df68c338d6719ed2b/app%2Fassets%2Fjavascripts%2Fide%2Fcomponents%2Frepo_editor.vue#L0-1)
+ used in the Web IDE. This component interfaces with a Singleton [Editor](https://gitlab.com/gitlab-org/gitlab/-/blob/862ad57c44ec758ef3942ac2e7a2bd40a37a9c59/app%2Fassets%2Fjavascripts%2Fide%2Flib%2Feditor.js#L21)
+ which manages some state for working with Monaco. Because of the Singleton nature of the Editor class,
+ the component `RepoEditor` is now forced to be a Singleton as well. Multiple instances of this component
+ would cause production issues because no one truly owns the instance of `Editor`.
+
+#### Why is the Singleton pattern popular in other languages like Java?
+
+This is because of the limitations of languages like Java where everything has to be wrapped
+in a class. In JavaScript we have things like object and function literals where we can solve
+many problems with a module that exports utility functions.
+
+#### When could the Singleton pattern be actually appropriate?**
+
+Singletons solve the problem of enforcing there to be only 1 instance of a thing. It's possible
+that a Singleton could be appropriate in the following rare cases:
+
+- We need to manage some resource that **MUST** have just 1 instance (that is, some hardware restriction).
+- There is a real [cross-cutting concern](https://en.wikipedia.org/wiki/Cross-cutting_concern) (for example, logging) and a Singleton provides the simplest API.
+
+Even in these scenarios, consider avoiding the Singleton pattern.
+
+#### What alternatives are there to the Singleton pattern?
+
+##### Utility Functions
+
+When no state needs to be managed, we can export utility functions from a module without
+messing with any class instantiation.
+
+```javascript
+// bad - Singleton
+export class ThingUtils {
+ static create() {
+ if(this.instance) {
+ return this.instance;
+ }
+
+ this.instance = new ThingUtils();
+ return this.instance;
+ }
+
+ bar() { /* ... */ }
+
+ fuzzify(id) { /* ... */ }
+}
+
+// good - Utility functions
+export const bar = () => { /* ... */ };
+
+export const fuzzify = (id) => { /* ... */ };
+```
+
+##### Dependency Injection
+
+[Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) is an approach which breaks
+coupling by declaring a module's dependencies to be injected from outside the module (for example, through constructor parameters, a bona-fide Dependency Injection framework, and even in Vue `provide/inject`).
+
+```javascript
+// bad - Vue component coupled to Singleton
+export default {
+ created() {
+ this.mediator = MyFooMediator.getInstance();
+ },
+};
+
+// good - Vue component declares dependency
+export default {
+ inject: ['mediator']
+};
+```
+
+```javascript
+// bad - We're not sure where the singleton is in it's lifecycle so we init it here.
+export class Foo {
+ constructor() {
+ Bar.getInstance().init();
+ }
+
+ stuff() {
+ return Bar.getInstance().doStuff();
+ }
+}
+
+// good - Lets receive this dependency as a constructor argument.
+// It's also not our responsibility to manage the lifecycle.
+export class Foo {
+ constructor(bar) {
+ this.bar = bar;
+ }
+
+ stuff() {
+ return this.bar.doStuff();
+ }
+}
+```
+
+In this example, the lifecycle and implementation details of `mediator` are all managed
+**outside** the component (most likely the page entrypoint).
diff --git a/doc/development/fe_guide/tech_stack.md b/doc/development/fe_guide/tech_stack.md
new file mode 100644
index 00000000000..9c0d50ea7bd
--- /dev/null
+++ b/doc/development/fe_guide/tech_stack.md
@@ -0,0 +1,11 @@
+---
+stage: none
+group: unassigned
+info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/product/ux/technical-writing/#assignments
+---
+
+# Tech Stack
+
+For an exhaustive list of all the technology that we use, simply check our [latest `package.json` file](https://gitlab.com/gitlab-org/gitlab/-/blob/master/package.json?ref_type=heads).
+
+Each navigation item in this section is a guide for that specific technology.
diff --git a/doc/development/file_storage.md b/doc/development/file_storage.md
index c346d55f639..39833a441ee 100644
--- a/doc/development/file_storage.md
+++ b/doc/development/file_storage.md
@@ -56,7 +56,7 @@ they are still not 100% standardized. You can see them below:
CI Artifacts and LFS Objects behave differently in CE and EE. In CE they inherit the `GitlabUploader`
while in EE they inherit the `ObjectStorage` and store files in and S3 API compatible object store.
-In the case of Issues/MR/Notes Markdown attachments, there is a different approach using the [Hashed Storage](../administration/repository_storage_types.md) layout,
+In the case of Issues/MR/Notes Markdown attachments, there is a different approach using the [Hashed Storage](../administration/repository_storage_paths.md) layout,
instead of basing the path into a mutable variable `:project_path_with_namespace`, it's possible to use the
hash of the project ID instead, if project migrates to the new approach (introduced in 10.2).
diff --git a/doc/development/git_object_deduplication.md b/doc/development/git_object_deduplication.md
index 961bfca0d9b..65d2338cd65 100644
--- a/doc/development/git_object_deduplication.md
+++ b/doc/development/git_object_deduplication.md
@@ -46,7 +46,7 @@ reliable decide if an object is no longer needed.
### Git alternates in GitLab: pool repositories
-GitLab organizes this object borrowing by [creating special **pool repositories**](../administration/repository_storage_types.md)
+GitLab organizes this object borrowing by [creating special **pool repositories**](../administration/repository_storage_paths.md)
which are hidden from the user. We then use Git
alternates to let a collection of project repositories borrow from a
single pool repository. We call such a collection of project
@@ -101,7 +101,7 @@ are as follows:
### Assumptions
-- All repositories in a pool must use [hashed storage](../administration/repository_storage_types.md).
+- All repositories in a pool must use [hashed storage](../administration/repository_storage_paths.md).
This is so that we don't have to ever worry about updating paths in
`object/info/alternates` files.
- All repositories in a pool must be on the same Gitaly storage shard.
diff --git a/doc/development/testing_guide/best_practices.md b/doc/development/testing_guide/best_practices.md
index 65787f7a355..d785cd3388d 100644
--- a/doc/development/testing_guide/best_practices.md
+++ b/doc/development/testing_guide/best_practices.md
@@ -1225,7 +1225,7 @@ specs, so created repositories accumulate in this directory over the
lifetime of the process. Deleting them is expensive, but this could lead to
pollution unless carefully managed.
-To avoid this, [hashed storage](../../administration/repository_storage_types.md)
+To avoid this, [hashed storage](../../administration/repository_storage_paths.md)
is enabled in the test suite. This means that repositories are given a unique
path that depends on their project's ID. Because the project IDs are not reset
between specs, each spec gets its own repository on disk,
diff --git a/doc/operations/img/tracing_details_v16_3.png b/doc/operations/img/tracing_details_v16_3.png
new file mode 100644
index 0000000000000000000000000000000000000000..b6d0e89c6ec54faf3db828d18de91e77e7a0fa8e
GIT binary patch
literal 128734
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