Commit 46da30e6 authored by Vijay Pandian's avatar Vijay Pandian

Merge branch 'master' into 34013-clarifying-only-key-functionality-yaml-doc

parents 8e26cfe7 da1153ee
......@@ -383,7 +383,7 @@ group :development, :test do
gem 'simple_po_parser', '~> 1.1.2', require: false
gem 'timecop', '~> 0.8.0'
gem 'timecop', '~> 0.9.1'
gem 'png_quantizator', '~> 0.2.1', require: false
......@@ -419,7 +419,8 @@ end
gem 'octokit', '~> 4.15'
gem 'mail_room', '~> 0.10.0'
# https://gitlab.com/gitlab-org/gitlab/issues/207207
gem 'gitlab-mail_room', '~> 0.0.3', require: 'mail_room'
gem 'email_reply_trimmer', '~> 0.1'
gem 'html2text'
......
......@@ -388,6 +388,7 @@ GEM
opentracing (~> 0.4)
redis (> 3.0.0, < 5.0.0)
gitlab-license (1.0.0)
gitlab-mail_room (0.0.3)
gitlab-markup (1.7.0)
gitlab-net-dns (0.9.1)
gitlab-puma (4.3.1.gitlab.2)
......@@ -616,7 +617,6 @@ GEM
lumberjack (1.0.13)
mail (2.7.1)
mini_mime (>= 0.1.1)
mail_room (0.10.0)
marcel (0.3.3)
mimemagic (~> 0.3.2)
marginalia (1.8.0)
......@@ -1065,7 +1065,7 @@ GEM
thread_safe (0.3.6)
thrift (0.11.0.0)
tilt (2.0.10)
timecop (0.8.1)
timecop (0.9.1)
timfel-krb5-auth (0.8.3)
toml (0.2.0)
parslet (~> 1.8.0)
......@@ -1235,6 +1235,7 @@ DEPENDENCIES
gitlab-chronic (~> 0.10.5)
gitlab-labkit (= 0.10.0)
gitlab-license (~> 1.0)
gitlab-mail_room (~> 0.0.3)
gitlab-markup (~> 1.7.0)
gitlab-net-dns (~> 0.9.1)
gitlab-puma (~> 4.3.1.gitlab.2)
......@@ -1284,7 +1285,6 @@ DEPENDENCIES
loofah (~> 2.2)
lru_redux
mail (= 2.7.1)
mail_room (~> 0.10.0)
marginalia (~> 1.8.0)
memory_profiler (~> 0.9)
method_source (~> 0.8)
......@@ -1387,7 +1387,7 @@ DEPENDENCIES
sys-filesystem (~> 1.1.6)
test-prof (~> 0.10.0)
thin (~> 1.7.0)
timecop (~> 0.8.0)
timecop (~> 0.9.1)
toml-rb (~> 1.0.0)
truncato (~> 0.7.11)
u2f (~> 0.2.1)
......
......@@ -166,6 +166,7 @@ export default {
:href="lineHref"
@click="setHighlightedRow(lineCode)"
>
{{ lineNumber }}
</a>
<diff-gutter-avatars
v-if="shouldShowAvatarsOnGutter"
......
......@@ -28,6 +28,8 @@ export const getUserData = state => state.userData || {};
export const getUserDataByProp = state => prop => state.userData && state.userData[prop];
export const descriptionVersion = state => state.descriptionVersion;
export const notesById = state =>
state.discussions.reduce((acc, note) => {
note.notes.every(n => Object.assign(acc, { [n.id]: n }));
......
......@@ -54,8 +54,8 @@ export default {
};
},
computed: {
...mapGetters(['targetNoteHash']),
...mapState(['descriptionVersion', 'isLoadingDescriptionVersion']),
...mapGetters(['targetNoteHash', 'descriptionVersion']),
...mapState(['isLoadingDescriptionVersion']),
noteAnchorId() {
return `note_${this.note.id}`;
},
......
......@@ -485,10 +485,6 @@ table.code {
}
}
}
&:not(.js-unfold-bottom) a::before {
content: attr(data-linenumber);
}
}
&.line_content {
......
......@@ -34,6 +34,7 @@ class ApplicationController < ActionController::Base
before_action :check_impersonation_availability
before_action :required_signup_info
around_action :sessionless_bypass_admin_mode!, if: :sessionless_user?
around_action :set_current_context
around_action :set_locale
around_action :set_session_storage
......
......@@ -5,12 +5,6 @@
# Controller concern to handle PAT, RSS, and static objects token authentication methods
#
module SessionlessAuthentication
extend ActiveSupport::Concern
included do
before_action :enable_admin_mode!, if: :sessionless_user?
end
# This filter handles personal access tokens, atom requests with rss tokens, and static object tokens
def authenticate_sessionless_user!(request_format)
user = Gitlab::Auth::RequestAuthenticator.new(request).find_sessionless_user(request_format)
......@@ -32,9 +26,9 @@ module SessionlessAuthentication
end
end
def enable_admin_mode!
return unless Feature.enabled?(:user_mode_in_session)
def sessionless_bypass_admin_mode!(&block)
return yield unless Feature.enabled?(:user_mode_in_session)
current_user_mode.enable_sessionless_admin_mode!
Gitlab::Auth::CurrentUserMode.bypass_session!(current_user.id, &block)
end
end
......@@ -15,6 +15,11 @@ class GraphqlController < ApplicationController
before_action :authorize_access_api!
before_action(only: [:execute]) { authenticate_sessionless_user!(:api) }
# Since we deactivate authentication from the main ApplicationController and
# defer it to :authorize_access_api!, we need to override the bypass session
# callback execution order here
around_action :sessionless_bypass_admin_mode!, if: :sessionless_user?
def execute
result = multiplex? ? execute_multiplex : execute_query
......
......@@ -195,7 +195,8 @@ class GroupsController < Groups::ApplicationController
:require_two_factor_authentication,
:two_factor_grace_period,
:project_creation_level,
:subgroup_creation_level
:subgroup_creation_level,
:default_branch_protection
]
end
......
......@@ -20,6 +20,10 @@ module CacheMarkdownField
false
end
def can_cache_field?(field)
true
end
# Returns the default Banzai render context for the cached markdown field.
def banzai_render_context(field)
raise ArgumentError.new("Unknown field: #{field.inspect}") unless
......@@ -38,17 +42,23 @@ module CacheMarkdownField
context
end
# Update every column in a row if any one is invalidated, as we only store
# one version per row
def refresh_markdown_cache
def rendered_field_content(markdown_field)
return unless can_cache_field?(markdown_field)
options = { skip_project_check: skip_project_check? }
Banzai::Renderer.cacheless_render_field(self, markdown_field, options)
end
# Update every applicable column in a row if any one is invalidated, as we only store
# one version per row
def refresh_markdown_cache
updates = cached_markdown_fields.markdown_fields.map do |markdown_field|
[
cached_markdown_fields.html_field(markdown_field),
Banzai::Renderer.cacheless_render_field(self, markdown_field, options)
rendered_field_content(markdown_field)
]
end.to_h
updates['cached_markdown_version'] = latest_cached_markdown_version
updates.each { |field, data| write_markdown_field(field, data) }
......
......@@ -135,7 +135,7 @@ class Deployment < ApplicationRecord
end
def create_ref
project.repository.create_ref(ref, ref_path)
project.repository.create_ref(sha, ref_path)
end
def invalidate_cache
......@@ -280,12 +280,12 @@ class Deployment < ApplicationRecord
errors.add(:ref, _('The branch or tag does not exist'))
end
private
def ref_path
File.join(environment.ref_path, 'deployments', iid.to_s)
end
private
def legacy_finished_at
self.created_at if success? && !read_attribute(:finished_at)
end
......
......@@ -193,15 +193,6 @@ class Environment < ApplicationRecord
folder_name == "production"
end
def first_deployment_for(commit_sha)
ref = project.repository.ref_name_for_sha(ref_path, commit_sha)
return unless ref
deployment_iid = ref.split('/').last
deployments.find_by(iid: deployment_iid)
end
def ref_path
"refs/#{Repository::REF_ENVIRONMENTS}/#{slug}"
end
......
......@@ -406,11 +406,15 @@ class Group < Namespace
end
def ci_variables_for(ref, project)
list_of_ids = [self] + ancestors
variables = Ci::GroupVariable.where(group: list_of_ids)
variables = variables.unprotected unless project.protected_for?(ref)
variables = variables.group_by(&:group_id)
list_of_ids.reverse.flat_map { |group| variables[group.id] }.compact
cache_key = "ci_variables_for:group:#{self&.id}:project:#{project&.id}:ref:#{ref}"
::Gitlab::SafeRequestStore.fetch(cache_key) do
list_of_ids = [self] + ancestors
variables = Ci::GroupVariable.where(group: list_of_ids)
variables = variables.unprotected unless project.protected_for?(ref)
variables = variables.group_by(&:group_id)
list_of_ids.reverse.flat_map { |group| variables[group.id] }.compact
end
end
def group_member(user)
......
......@@ -130,7 +130,7 @@ class Namespace < ApplicationRecord
return unless host.ends_with?(gitlab_host)
name = host.delete_suffix(gitlab_host)
Namespace.find_by_full_path(name)
Namespace.find_by_path(name)
end
# overridden in ee
......@@ -139,6 +139,10 @@ class Namespace < ApplicationRecord
end
end
def default_branch_protection
super || Gitlab::CurrentSettings.default_branch_protection
end
def visibility_level_field
:visibility_level
end
......
......@@ -1963,6 +1963,14 @@ class Project < ApplicationRecord
end
def ci_variables_for(ref:, environment: nil)
cache_key = "ci_variables_for:project:#{self&.id}:ref:#{ref}:environment:#{environment}"
::Gitlab::SafeRequestStore.fetch(cache_key) do
uncached_ci_variables_for(ref: ref, environment: environment)
end
end
def uncached_ci_variables_for(ref:, environment: nil)
result = if protected_for?(ref)
variables
else
......@@ -2351,6 +2359,12 @@ class Project < ApplicationRecord
Gitlab::Routing.url_helpers.revoke_project_deploy_token_path(self, token)
end
def default_branch_protected?
branch_protection = Gitlab::Access::BranchProtection.new(self.namespace.default_branch_protection)
branch_protection.fully_protected? || branch_protection.developer_can_merge?
end
private
def closest_namespace_setting(name)
......
......@@ -11,7 +11,8 @@ class ProtectedBranch < ApplicationRecord
def self.protected_ref_accessible_to?(ref, user, project:, action:, protected_refs: nil)
# Maintainers, owners and admins are allowed to create the default branch
if default_branch_protected? && project.empty_repo?
if project.empty_repo? && project.default_branch_protected?
return true if user.admin? || project.team.max_member_access(user.id) > Gitlab::Access::DEVELOPER
end
......@@ -20,7 +21,7 @@ class ProtectedBranch < ApplicationRecord
# Check if branch name is marked as protected in the system
def self.protected?(project, ref_name)
return true if project.empty_repo? && default_branch_protected?
return true if project.empty_repo? && project.default_branch_protected?
self.matching(ref_name, protected_refs: protected_refs(project)).present?
end
......@@ -33,11 +34,6 @@ class ProtectedBranch < ApplicationRecord
end
end
def self.default_branch_protected?
Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_FULL ||
Gitlab::CurrentSettings.default_branch_protection == Gitlab::Access::PROTECTION_DEV_CAN_MERGE
end
def self.protected_refs(project)
project.protected_branches
end
......
# frozen_string_literal: true
module ResourceEventTools
extend ActiveSupport::Concern
class ResourceEvent < ApplicationRecord
include Gitlab::Utils::StrongMemoize
include Importable
included do
belongs_to :user
self.abstract_class = true
validates :user, presence: { unless: :importing? }, on: :create
validates :user, presence: { unless: :importing? }, on: :create
validate :exactly_one_issuable
belongs_to :user
scope :created_after, ->(time) { where('created_at > ?', time) }
scope :created_after, ->(time) { where('created_at > ?', time) }
def discussion_id
strong_memoize(:discussion_id) do
Digest::SHA1.hexdigest(discussion_id_key.join("-"))
end
end
private
def discussion_id_key
[self.class.name, created_at, user_id]
end
def exactly_one_issuable
......
# frozen_string_literal: true
class ResourceLabelEvent < ApplicationRecord
include Importable
include Gitlab::Utils::StrongMemoize
class ResourceLabelEvent < ResourceEvent
include CacheMarkdownField
include ResourceEventTools
cache_markdown_field :reference
......@@ -13,8 +10,11 @@ class ResourceLabelEvent < ApplicationRecord
belongs_to :label
scope :inc_relations, -> { includes(:label, :user) }
scope :by_issue, ->(issue) { where(issue_id: issue.id) }
scope :by_merge_request, ->(merge_request) { where(merge_request_id: merge_request.id) }
validates :label, presence: { unless: :importing? }, on: :create
validate :exactly_one_issuable
after_save :expire_etag_cache
after_destroy :expire_etag_cache
......@@ -41,12 +41,6 @@ class ResourceLabelEvent < ApplicationRecord
issue || merge_request
end
def discussion_id(resource = nil)
strong_memoize(:discussion_id) do
Digest::SHA1.hexdigest(discussion_id_key.join("-"))
end
end
def project
issuable.project
end
......@@ -109,10 +103,6 @@ class ResourceLabelEvent < ApplicationRecord
def resource_parent
issuable.project || issuable.group
end
def discussion_id_key
[self.class.name, created_at, user_id]
end
end
ResourceLabelEvent.prepend_if_ee('EE::ResourceLabelEvent')
# frozen_string_literal: true
class ResourceMilestoneEvent < ApplicationRecord
include Gitlab::Utils::StrongMemoize
include Importable
include ResourceEventTools
class ResourceMilestoneEvent < ResourceEvent
belongs_to :issue
belongs_to :merge_request
belongs_to :milestone
......@@ -12,6 +8,8 @@ class ResourceMilestoneEvent < ApplicationRecord
scope :by_issue, ->(issue) { where(issue_id: issue.id) }
scope :by_merge_request, ->(merge_request) { where(merge_request_id: merge_request.id) }
validate :exactly_one_issuable
enum action: {
add: 1,
remove: 2
......@@ -23,8 +21,4 @@ class ResourceMilestoneEvent < ApplicationRecord
def self.issuable_attrs
%i(issue merge_request).freeze
end
def resource
issue || merge_request
end
end
# frozen_string_literal: true
class ResourceWeightEvent < ApplicationRecord
include Gitlab::Utils::StrongMemoize
validates :user, presence: true
class ResourceWeightEvent < ResourceEvent
validates :issue, presence: true
belongs_to :user
belongs_to :issue
scope :by_issue, ->(issue) { where(issue_id: issue.id) }
scope :created_after, ->(time) { where('created_at > ?', time) }
def discussion_id(resource = nil)
strong_memoize(:discussion_id) do
Digest::SHA1.hexdigest(discussion_id_key.join("-"))
end
end
private
def discussion_id_key
[self.class.name, created_at, user_id]
end
end
......@@ -301,6 +301,10 @@ class Snippet < ApplicationRecord
repository.update!(shard_name: repository_storage, disk_path: disk_path)
end
def can_cache_field?(field)
field != :content || MarkupHelper.gitlab_markdown?(file_name)
end
class << self
# Searches for snippets with a matching title or file name.
#
......
......@@ -2,7 +2,7 @@
module UserBotTypeEnums
def self.bots
# When adding a new key, please ensure you are not conflicting with EE-only keys in app/models/user_bot_types_enums.rb
# When adding a new key, please ensure you are not conflicting with EE-only keys in app/models/user_bot_type_enums.rb
{
alert_bot: 2
}
......
......@@ -21,8 +21,19 @@ module Issues
def process_csv
csv_data = @csv_io.open(&:read).force_encoding(Encoding::UTF_8)
CSV.new(csv_data, col_sep: detect_col_sep(csv_data.lines.first), headers: true).each.with_index(2) do |row, line_no|
issue = Issues::CreateService.new(@project, @user, title: row[0], description: row[1]).execute
csv_parsing_params = {
col_sep: detect_col_sep(csv_data.lines.first),
headers: true,
header_converters: :symbol
}
CSV.new(csv_data, csv_parsing_params).each.with_index(2) do |row, line_no|
issue_attributes = {
title: row[:title],
description: row[:description]
}
issue = Issues::CreateService.new(@project, @user, issue_attributes).execute
if issue.persisted?
@results[:success] += 1
......
......@@ -11,7 +11,7 @@ module Projects
@project = project
@default_branch_protection = Gitlab::Access::BranchProtection
.new(Gitlab::CurrentSettings.default_branch_protection)
.new(project.namespace.default_branch_protection)
end
def execute
......
......@@ -2,9 +2,8 @@
= form_errors(@application_setting)
%fieldset
.form-group
= f.label :default_branch_protection, class: 'label-bold'
= f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, @application_setting.default_branch_protection), {}, class: 'form-control'
= render 'shared/default_branch_protection', f: f, selected_level: @application_setting.default_branch_protection
.form-group
= f.label s_('ProjectCreationLevel|Default project creation protection'), class: 'label-bold'
= f.select :default_project_creation, options_for_select(Gitlab::Access.project_creation_options, @application_setting.default_project_creation), {}, class: 'form-control'
......
......@@ -33,6 +33,7 @@
= render_if_exists 'groups/settings/ip_restriction', f: f, group: @group
= render_if_exists 'groups/settings/allowed_email_domain', f: f, group: @group
= render 'groups/settings/lfs', f: f
= render 'shared/default_branch_protection', f: f, selected_level: @group.default_branch_protection
= render 'groups/settings/project_creation_level', f: f, group: @group
= render 'groups/settings/subgroup_creation_level', f: f, group: @group
= render 'groups/settings/two_factor_auth', f: f
......
.form-group
= f.label :default_branch_protection, class: 'label-bold'
= f.select :default_branch_protection, options_for_select(Gitlab::Access.protection_options, selected_level), {}, class: 'form-control'
......@@ -19,7 +19,7 @@ get_mail_room_pid()
start()
{
bin/daemon_with_pidfile $mail_room_pidfile bundle exec mail_room -q -c $mail_room_config >> $mail_room_logfile 2>&1
bin/daemon_with_pidfile $mail_room_pidfile bundle exec mail_room --log-exit-as json -q -c $mail_room_config >> $mail_room_logfile 2>&1
}
stop()
......
---
title: Allow issues/merge_requests as an issuable_type in Insights configuration
merge_request: 26061
author:
type: added
---
title: Use of sha instead of ref when creating a new ref on deployment creation.
merge_request: 23170
author:
type: fixed
---
title: Replace content_viewer_spec setTimeouts with semantic actions / events
merge_request:
author: Oregand
type: other
---
title: Replace line diff number css selector with actual HTML inside MRs
merge_request:
author: Oregand
type: other
---
title: Improve error messages of failed migrations
merge_request: 25457
author:
type: changed
---
title: Validate actor against CODEOWNERS entries
merge_request:
author:
type: fixed
---
title: Fix Snippet content incorrectly caching
merge_request: 25985
author:
type: fixed
---
title: 'Code Review Analytics: Fix review time display'
merge_request: 26057
author:
type: fixed
---
title: Fix fixtures for Error Tracking Web UI
merge_request: 26233
author: Takuya Noguchi
type: other
---
title: 'Add a bulk processor for elasticsearch incremental updates'
merge_request: 24298
author:
type: added
---
title: Introduce default branch protection at the group level
merge_request: 24426
author:
type: added
---
title: Move issues routes under /-/ scope
merge_request: 24791
author:
type: changed
---
title: Add link to dependency proxy docs on the dependency proxy page
merge_request: 26092
author:
type: changed
---
title: Fix issues missing on epic's page after project import
merge_request: 26099
author:
type: fixed
---
title: Fix issue importer so it matches issue export format
merge_request: 25896
author:
type: fixed
---
title: Fix dev vulnerabilities seeder
merge_request: 26169
author:
type: fixed
---
title: 'Geo: Show secondary-only setting on only on secondaries'
merge_request: 26029
author:
type: fixed
---
title: Sessionless and API endpoints bypass session for admin mode
merge_request: 25056
author: Diego Louzán
type: changed
---
title: Memoize loading of CI variables
merge_request: 26147
author:
type: performance
---
title: Disable Marginalia line backtrace in production
merge_request: 26199
author:
type: performance
......@@ -292,6 +292,13 @@ module Gitlab
initializer :move_initializers, before: :load_config_initializers, after: :let_zeitwerk_take_over do
end
# We need this for initializers that need to be run before Zeitwerk is loaded
initializer :before_zeitwerk, before: :let_zeitwerk_take_over, after: :prepend_helpers_path do
Dir[Rails.root.join('config/initializers_before_autoloader/*.rb')].sort.each do |initializer|
load_config_initializer(initializer)
end
end
config.after_initialize do
Rails.application.reload_routes!
......
......@@ -454,6 +454,11 @@ production: &base
pseudonymizer_worker:
cron: "0 * * * *"
# Elasticsearch bulk updater for incremental updates.
# NOTE: This will only take effect if elasticsearch is enabled.
elastic_index_bulk_cron_worker:
cron: "*/1 * * * *"
registry:
# enabled: true
# host: registry.example.com
......
......@@ -9,7 +9,13 @@ require 'marginalia'
# Refer: https://github.com/basecamp/marginalia/blob/v1.8.0/lib/marginalia/railtie.rb#L67
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(Gitlab::Marginalia::ActiveRecordInstrumentation)
Marginalia::Comment.components = [:application, :controller, :action, :correlation_id, :jid, :job_class, :line]
Marginalia::Comment.components = [:application, :controller, :action, :correlation_id, :jid, :job_class]
# As mentioned in https://github.com/basecamp/marginalia/pull/93/files,
# adding :line has some overhead because a regexp on the backtrace has
# to be run on every SQL query. Only enable this in development because
# we've seen it slow things down.
Marginalia::Comment.components << :line if Rails.env.development?
Gitlab::Marginalia.set_application_name
......
......@@ -537,6 +537,9 @@ Gitlab.ee do
Settings.cron_jobs['update_max_seats_used_for_gitlab_com_subscriptions_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['update_max_seats_used_for_gitlab_com_subscriptions_worker']['cron'] ||= '0 12 * * *'
Settings.cron_jobs['update_max_seats_used_for_gitlab_com_subscriptions_worker']['job_class'] = 'UpdateMaxSeatsUsedForGitlabComSubscriptionsWorker'
Settings.cron_jobs['elastic_index_bulk_cron_worker'] ||= Settingslogic.new({})
Settings.cron_jobs['elastic_index_bulk_cron_worker']['cron'] ||= '*/1 * * * *'
Settings.cron_jobs['elastic_index_bulk_cron_worker']['job_class'] ||= 'ElasticIndexBulkCronWorker'
end
#
......
......@@ -121,10 +121,7 @@ Rails.application.routes.draw do
draw :country
draw :country_state
draw :subscription
constraints(-> (*) { Gitlab::Analytics.any_features_enabled? }) do
draw :analytics
end
draw :analytics
end
if ENV['GITLAB_CHAOS_SECRET'] || Rails.env.development? || Rails.env.test?
......
......@@ -279,6 +279,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end
end
draw :issues
draw :merge_requests
# The wiki and repository routing contains wildcard characters so
......@@ -401,12 +402,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
# Unscoped route. It will be replaced with redirect to /-/issues/
# Issue https://gitlab.com/gitlab-org/gitlab/issues/118849
draw :issues
# To ensure an old unscoped routing is used for the UI we need to
# add prefix 'as' to the scope routing and place it below original routing.
# Issue https://gitlab.com/gitlab-org/gitlab/issues/118849
scope '-', as: 'scoped' do
scope as: 'deprecated' do
draw :issues
end
......
# frozen_string_literal: true
class AddDefaultBranchProtectionToNamespaces < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def change
with_lock_retries do
add_column :namespaces, :default_branch_protection, :integer, limit: 2
end
end
end
......@@ -2762,6 +2762,7 @@ ActiveRecord::Schema.define(version: 2020_02_26_162723) do
t.integer "max_pages_size"
t.integer "max_artifacts_size"
t.boolean "mentions_disabled"
t.integer "default_branch_protection", limit: 2
t.index ["created_at"], name: "index_namespaces_on_created_at"
t.index ["custom_project_templates_group_id", "type"], name: "index_namespaces_on_custom_project_templates_group_id_and_type", where: "(custom_project_templates_group_id IS NOT NULL)"
t.index ["file_template_project_id"], name: "index_namespaces_on_file_template_project_id"
......
......@@ -42,6 +42,7 @@ The OpenID Connect will provide you with a client details and secret for you to
'discovery' => true,
'client_auth_method' => 'query',
'uid_field' => '<uid_field>',
'send_scope_to_token_endpoint' => 'false',
'client_options' => {
'identifier' => '<your_oidc_client_id>',
'secret' => '<your_oidc_client_secret>',
......@@ -65,6 +66,7 @@ The OpenID Connect will provide you with a client details and secret for you to
discovery: true,
client_auth_method: 'query',
uid_field: '<uid_field>',
send_scope_to_token_endpoint: false,
client_options: {
identifier: '<your_oidc_client_id>',
secret: '<your_oidc_client_secret>',
......@@ -92,6 +94,8 @@ The OpenID Connect will provide you with a client details and secret for you to
- If not specified, defaults to `basic`.
- `<uid_field>` (optional) is the field name from the `user_info` details that will be used as `uid` value. For example, `preferred_username`.
If this value is not provided or the field with the configured value is missing from the `user_info` details, the `uid` will use the `sub` field.
- `send_scope_to_token_endpoint` is `true` by default. In other words, the `scope` parameter is normally included in requests to the token endpoint.
However, if your OpenID Connect provider does not accept the `scope` parameter in such requests, set this to `false`.
- `client_options` are the OpenID Connect client-specific options. Specifically:
- `identifier` is the client identifier as configured in the OpenID Connect service provider.
- `secret` is the client secret as configured in the OpenID Connect service provider.
......
......@@ -42,21 +42,6 @@ Now that the Okta app is configured, it's time to enable it in GitLab.
## Configure GitLab
1. On your GitLab server, open the configuration file:
**For Omnibus GitLab installations**
```shell
sudo editor /etc/gitlab/gitlab.rb
```
**For installations from source**
```shell
cd /home/git/gitlab
sudo -u git -H editor config/gitlab.yml
```
1. See [Initial OmniAuth Configuration](../../integration/omniauth.md#initial-omniauth-configuration)
for initial settings.
......@@ -66,13 +51,19 @@ Now that the Okta app is configured, it's time to enable it in GitLab.
**For Omnibus GitLab installations**
Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['omniauth_allow_single_sign_on'] = ['saml']
gitlab_rails['omniauth_block_auto_created_users'] = false
```
---
**For installations from source**
Edit `config/gitlab.yml`:
```yaml
allow_single_sign_on: ["saml"]
block_auto_created_users: false
......@@ -83,15 +74,21 @@ Now that the Okta app is configured, it's time to enable it in GitLab.
**For Omnibus GitLab installations**
Edit `/etc/gitlab/gitlab.rb`:
```ruby
gitlab_rails['omniauth_auto_link_saml_user'] = true
```
---
**For installations from source**
```yaml
auto_link_saml_user: true
```
Edit `config/gitlab.yml`:
```yaml
auto_link_saml_user: true
```
1. Add the provider configuration.
......
......@@ -6,9 +6,9 @@ components can read or write Git data. GitLab components that access Git
repositories (GitLab Rails, GitLab Shell, GitLab Workhorse, etc.) act as clients
to Gitaly. End users do not have direct access to Gitaly.
In the rest of this page, Gitaly server is referred to the standalone node that
only runs Gitaly, and Gitaly client to the GitLab Rails node that runs all other
processes except Gitaly.
On this page, *Gitaly server* refers to a standalone node that only runs Gitaly
and *Gitaly client* is a GitLab Rails app node that runs all other processes
except Gitaly.
## Architecture
......@@ -20,7 +20,7 @@ Here's a high-level architecture overview of how Gitaly is used.
The Gitaly service itself is configured via a [TOML configuration file](reference.md).
In case you want to change some of its settings:
If you want to change any of its settings:
**For Omnibus GitLab**
......@@ -54,10 +54,6 @@ scenario, the [new repository indexer](../../integration/elasticsearch.md#elasti
needs to be enabled in your GitLab configuration. [Since GitLab v12.3](https://gitlab.com/gitlab-org/gitlab/issues/6481),
the new indexer becomes the default and no configuration is required.
NOTE: **Note:** While Gitaly can be used as a replacement for NFS, it's not recommended
to use EFS as it may impact GitLab's performance. Review the [relevant documentation](../high_availability/nfs.md#avoid-using-awss-elastic-file-system-efs)
for more details.
### Network architecture
The following list depicts what the network architecture of Gitaly is:
......@@ -568,30 +564,6 @@ server with the following settings.
1. Save the file and [restart GitLab](../restart_gitlab.md#installations-from-source).
## Eliminating NFS altogether
If you are planning to use Gitaly without NFS for your storage needs
and want to eliminate NFS from your environment altogether, there are
a few things that you need to do:
1. Make sure the [`git` user home directory](https://docs.gitlab.com/omnibus/settings/configuration.html#moving-the-home-directory-for-a-user) is on local disk.
1. Configure [database lookup of SSH keys](../operations/fast_ssh_key_lookup.md)
to eliminate the need for a shared `authorized_keys` file.
1. Configure [object storage for job artifacts](../job_artifacts.md#using-object-storage)
including [incremental logging](../job_logs.md#new-incremental-logging-architecture).
1. Configure [object storage for LFS objects](../lfs/lfs_administration.md#storing-lfs-objects-in-remote-object-storage).
1. Configure [object storage for uploads](../uploads.md#using-object-storage-core-only).
1. Configure [object storage for merge request diffs](../merge_request_diffs.md#using-object-storage).
1. Configure [object storage for packages](../packages/index.md#using-object-storage) (optional feature).
1. Configure [object storage for dependency proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature).
1. Configure [object storage for Mattermost](https://docs.mattermost.com/administration/config-settings.html#file-storage) (optional feature).
NOTE: **Note:**
One current feature of GitLab that still requires a shared directory (NFS) is
[GitLab Pages](../../user/project/pages/index.md).
There is [work in progress](https://gitlab.com/gitlab-org/gitlab-pages/issues/196)
to eliminate the need for NFS to support GitLab Pages.
## Limiting RPC concurrency
It can happen that CI clone traffic puts a large strain on your Gitaly
......
......@@ -22,11 +22,9 @@ If you use a cloud-managed service, or provide your own PostgreSQL:
1. Configure the GitLab application servers with the appropriate details.
This step is covered in [Configuring GitLab for HA](gitlab.md).
## PostgreSQL in a Scaled Environment
## PostgreSQL in a Scaled and Highly Available Environment
This section is relevant for [Scaled Architecture](README.md#scalable-architecture-examples)
environments including [Basic Scaling](README.md#basic-scaling) and
[Full Scaling](README.md#full-scaling).
This section is relevant for [Scalable and Highly Available Setups](README.md).
### Provide your own PostgreSQL instance **(CORE ONLY)**
......@@ -94,23 +92,6 @@ deploy the bundled PostgreSQL.
Advanced configuration options are supported and can be added if
needed.
Continue configuration of other components by going
[back to Scaled Architectures](README.md#scalable-architecture-examples)
## PostgreSQL with High Availability
This section is relevant for [High Availability Architecture](README.md#high-availability-architecture-examples)
environments including [Horizontal](README.md#horizontal),
[Hybrid](README.md#hybrid), and
[Fully Distributed](README.md#fully-distributed).
### Provide your own PostgreSQL instance **(CORE ONLY)**
If you want to use your own deployed PostgreSQL instance(s),
see [Provide your own PostgreSQL instance](#provide-your-own-postgresql-instance-core-only)
for more details. However, you can use the GitLab Omnibus package to easily
deploy the bundled PostgreSQL.
### High Availability with GitLab Omnibus **(PREMIUM ONLY)**
> Important notes:
......
......@@ -11,18 +11,15 @@ should consider using Gitaly on a separate node.
See the [Gitaly HA Epic](https://gitlab.com/groups/gitlab-org/-/epics/289) to
track plans and progress toward high availability support.
This document is relevant for [Scaled Architecture](README.md#scalable-architecture-examples)
environments and [High Availability Architecture](README.md#high-availability-architecture-examples).
This document is relevant for [Scalable and Highly Available Setups](README.md).
## Running Gitaly on its own server
See [Running Gitaly on its own server](../gitaly/index.md#running-gitaly-on-its-own-server)
in Gitaly documentation.
Continue configuration of other components by going back to:
- [Scaled Architectures](README.md#scalable-architecture-examples)
- [High Availability Architectures](README.md#high-availability-architecture-examples)
Continue configuration of other components by going back to the
[Scaling and High Availability](README.md#gitlab-components-and-configuration-instructions) page.
## Enable Monitoring
......
---
type: reference
---
# Cloud Object Storage
GitLab supports utilizing a Cloud Object Storage service over [NFS](nfs.md) for holding
numerous types of data. This is recommended in larger setups as object storage is
typically much more performant and reliable.
For configuring GitLab to use Object Storage refer to the following guides:
1. Make sure the [`git` user home directory](https://docs.gitlab.com/omnibus/settings/configuration.html#moving-the-home-directory-for-a-user) is on local disk.
1. Configure [database lookup of SSH keys](../operations/fast_ssh_key_lookup.md)
to eliminate the need for a shared `authorized_keys` file.
1. Configure [object storage for job artifacts](../job_artifacts.md#using-object-storage)
including [incremental logging](../job_logs.md#new-incremental-logging-architecture).
1. Configure [object storage for LFS objects](../lfs/lfs_administration.md#storing-lfs-objects-in-remote-object-storage).
1. Configure [object storage for uploads](../uploads.md#using-object-storage-core-only).
1. Configure [object storage for merge request diffs](../merge_request_diffs.md#using-object-storage).
1. Configure [object storage for packages](../packages/index.md#using-object-storage) (optional feature).
1. Configure [object storage for dependency proxy](../packages/dependency_proxy.md#using-object-storage) (optional feature).
NOTE: **Note:**
One current feature of GitLab that still requires a shared directory (NFS) is
[GitLab Pages](../../user/project/pages/index.md).
There is [work in progress](https://gitlab.com/gitlab-org/gitlab-pages/issues/196)
to eliminate the need for NFS to support GitLab Pages.
......@@ -20,11 +20,9 @@ The following are the requirements for providing your own Redis instance:
Note the Redis node's IP address or hostname, port, and password (if required).
These will be necessary when configuring the GitLab application servers later.
## Redis in a Scaled Environment
## Redis in a Scaled and Highly Available Environment
This section is relevant for [Scaled Architecture](README.md#scalable-architecture-examples)
environments including [Basic Scaling](README.md#basic-scaling) and
[Full Scaling](README.md#full-scaling).
This section is relevant for [Scalable and Highly Available Setups](README.md).
### Provide your own Redis instance **(CORE ONLY)**
......@@ -85,22 +83,8 @@ Omnibus:
Advanced configuration options are supported and can be added if
needed.
Continue configuration of other components by going
[back to Scaled Architectures](README.md#scalable-architecture-examples)
## Redis with High Availability
This section is relevant for [High Availability Architecture](README.md#high-availability-architecture-examples)
environments including [Horizontal](README.md#horizontal),
[Hybrid](README.md#hybrid), and
[Fully Distributed](README.md#fully-distributed).
### Provide your own Redis instance **(CORE ONLY)**
If you want to use your own deployed Redis instance(s),
see [Provide your own Redis instance](#provide-your-own-redis-instance-core-only)
for more details. However, you can use the GitLab Omnibus package to easily
deploy the bundled Redis.
Continue configuration of other components by going back to the
[Scaling and High Availability](README.md#gitlab-components-and-configuration-instructions) page.
### High Availability with GitLab Omnibus **(PREMIUM ONLY)**
......
......@@ -355,7 +355,7 @@ curl --head --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example
The response will then be:
```
```http
HTTP/1.1 200 OK
Cache-Control: no-cache
Content-Length: 1103
......@@ -415,7 +415,7 @@ curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab
The response header includes a link to the next page. For example:
```
```http
HTTP/1.1 200 OK
...
Link: <https://gitlab.example.com/api/v4/projects?pagination=keyset&per_page=50&order_by=id&sort=asc&id_after=42>; rel="next"
......@@ -540,7 +540,7 @@ Such errors appear in two cases:
When an attribute is missing, you will get something like:
```
```http
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
......@@ -551,7 +551,7 @@ Content-Type: application/json
When a validation error occurs, error messages will be different. They will
hold all details of validation errors:
```
```http
HTTP/1.1 400 Bad Request
Content-Type: application/json
{
......@@ -589,7 +589,7 @@ follows:
When you try to access an API URL that does not exist you will receive 404 Not Found.
```
```http
HTTP/1.1 404 Not Found
Content-Type: application/json
{
......
......@@ -42,6 +42,7 @@ GET /groups
"emails_disabled": null,
"mentions_disabled": null,
"lfs_enabled": true,
"default_branch_protection": 2,
"avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg",
"web_url": "http://localhost:3000/groups/foo-bar",
"request_access_enabled": false,
......@@ -76,6 +77,7 @@ GET /groups?statistics=true
"emails_disabled": null,
"mentions_disabled": null,
"lfs_enabled": true,
"default_branch_protection": 2,
"avatar_url": "http://localhost:3000/uploads/group/avatar/1/foo.jpg",
"web_url": "http://localhost:3000/groups/foo-bar",
"request_access_enabled": false,
......@@ -148,6 +150,7 @@ GET /groups/:id/subgroups
"emails_disabled": null,
"mentions_disabled": null,
"lfs_enabled": true,
"default_branch_protection": 2,
"avatar_url": "http://gitlab.example.com/uploads/group/avatar/1/foo.jpg",
"web_url": "http://gitlab.example.com/groups/foo-bar",
"request_access_enabled": false,
......@@ -493,9 +496,20 @@ Parameters:
| `lfs_enabled` | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
| `parent_id` | integer | no | The parent group ID for creating nested group. |
| `default_branch_protection` | integer | no | See [Options for `default_branch_protection`](#options-for-default_branch_protection). Default to the global level default branch protection setting. |
| `shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Pipeline minutes quota for this group. |
| `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group. |
### Options for `default_branch_protection`
The `default_branch_protection` attribute determines whether developers and maintainers can push to the applicable master branch, as described in the following table:
| Value | Description |
|-------|-------------------------------------------------------------------------------------------------------------|
| `0` | No protection. Developers and maintainers can: <br>- Push new commits<br>- Force push changes<br>- Delete the branch |
| `1` | Partial protection. Developers and maintainers can: <br>- Push new commits |
| `2` | Full protection. Only maintainers can: <br>- Push new commits |
## Transfer project to group
Transfer a project to the Group namespace. Available only to instance administrators, although an [alternative API endpoint](projects.md#transfer-a-project-to-a-new-namespace) is available which does not require instance administrator access. Transferring projects may fail when tagged packages exist in the project's repository.
......@@ -542,6 +556,7 @@ PUT /groups/:id
| `mentions_disabled` | boolean | no | Disable the capability of a group from getting mentioned |
| `lfs_enabled` (optional) | boolean | no | Enable/disable Large File Storage (LFS) for the projects in this group. |
| `request_access_enabled` | boolean | no | Allow users to request member access. |
| `default_branch_protection` | integer | no | See [Options for `default_branch_protection`](#options-for-default_branch_protection). |
| `file_template_project_id` | integer | no | **(PREMIUM)** The ID of a project to load custom file templates from. |
| `shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Pipeline minutes quota for this group. |
| `extra_shared_runners_minutes_limit` | integer | no | **(STARTER ONLY)** Extra pipeline minutes quota for this group. |
......
......@@ -6,7 +6,7 @@ You can read more about [triggering pipelines through the API](../ci/triggers/RE
Get a list of project's build triggers.
```
```plaintext
GET /projects/:id/triggers
```
......@@ -14,7 +14,7 @@ GET /projects/:id/triggers
|-----------|---------|----------|---------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
```
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/triggers"
```
......@@ -36,7 +36,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
Get details of project's build trigger.
```
```plaintext
GET /projects/:id/triggers/:trigger_id
```
......@@ -45,7 +45,7 @@ GET /projects/:id/triggers/:trigger_id
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `trigger_id` | integer | yes | The trigger id |
```
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/triggers/5"
```
......@@ -65,7 +65,7 @@ curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/a
Create a trigger for a project.
```
```plaintext
POST /projects/:id/triggers
```
......@@ -74,7 +74,7 @@ POST /projects/:id/triggers
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `description` | string | yes | The trigger name |
```
```shell
curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form description="my description" "https://gitlab.example.com/api/v4/projects/1/triggers"
```
......@@ -94,7 +94,7 @@ curl --request POST --header "PRIVATE-TOKEN: <your_access_token>" --form descrip
Update a trigger for a project.
```
```plaintext
PUT /projects/:id/triggers/:trigger_id
```
......@@ -104,7 +104,7 @@ PUT /projects/:id/triggers/:trigger_id
| `trigger_id` | integer | yes | The trigger id |
| `description` | string | no | The trigger name |
```
```shell
curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --form description="my description" "https://gitlab.example.com/api/v4/projects/1/triggers/10"
```
......@@ -124,7 +124,7 @@ curl --request PUT --header "PRIVATE-TOKEN: <your_access_token>" --form descript
Remove a project's build trigger.
```
```plaintext
DELETE /projects/:id/triggers/:trigger_id
```
......@@ -133,6 +133,6 @@ DELETE /projects/:id/triggers/:trigger_id
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user |
| `trigger_id` | integer | yes | The trigger id |
```
```shell
curl --request DELETE --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/triggers/5"
```
......@@ -9,7 +9,7 @@ GitLab supports links links to `http`, `https`, and `ftp` assets.
Get assets as links from a Release.
```
```plaintext
GET /projects/:id/releases/:tag_name/assets/links
```
......@@ -47,7 +47,7 @@ Example response:
Get an asset as a link from a Release.
```
```plaintext
GET /projects/:id/releases/:tag_name/assets/links/:link_id
```
......@@ -78,7 +78,7 @@ Example response:
Create an asset as a link from a Release.
```
```plaintext
POST /projects/:id/releases/:tag_name/assets/links
```
......@@ -114,7 +114,7 @@ Example response:
Update an asset as a link from a Release.
```
```plaintext
PUT /projects/:id/releases/:tag_name/assets/links/:link_id
```
......@@ -150,7 +150,7 @@ Example response:
Delete an asset as a link from a Release.
```
```plaintext
DELETE /projects/:id/releases/:tag_name/assets/links/:link_id
```
......
......@@ -122,7 +122,7 @@ Parameters:
| `userName` | string | yes | Username of the user. |
| `emails` | JSON string | yes | Work email. |
| `name` | JSON string | yes | Name of the user. |
| `meta` | string | no | Resource type (`User'). |
| `meta` | string | no | Resource type (`User`). |
Example request:
......
......@@ -751,7 +751,7 @@ Search within the specified project.
If a user is not a member of a project and the project is private, a `GET` request on that project will result to a `404` status code.
```
```plaintext
GET /projects/:id/search
```
......
......@@ -1145,7 +1145,7 @@ Parameters:
| `merge_requests_events` | boolean | false | Enable notifications for merge request events |
| `tag_push_events` | boolean | false | Enable notifications for tag push events |
| `note_events` | boolean | false | Enable notifications for note events |
| `confidental_note_events` | boolean | false | Enable notifications for confidential note events |
| `confidential_note_events` | boolean | false | Enable notifications for confidential note events |
| `pipeline_events` | boolean | false | Enable notifications for pipeline events |
| `wiki_page_events` | boolean | false | Enable notifications for wiki page events |
......
......@@ -10,7 +10,7 @@ administrator in order to perform this action.
List the current [application settings](#list-of-settings-that-can-be-accessed-via-api-calls)
of the GitLab instance.
```
```plaintext
GET /application/settings
```
......@@ -90,7 +90,7 @@ the `file_template_project_id`, `deletion_adjourned_period`, or the `geo_node_al
Use an API call to modify GitLab instance
[application settings](#list-of-settings-that-can-be-accessed-via-api-calls).
```
```plaintext
PUT /application/settings
```
......
......@@ -10,7 +10,7 @@ of Sidekiq, its jobs, queues, and processes.
List information about all the registered queues, their backlog and their
latency.
```
```plaintext
GET /sidekiq/queue_metrics
```
......@@ -35,7 +35,7 @@ Example response:
List information about all the Sidekiq workers registered to process your queues.
```
```plaintext
GET /sidekiq/process_metrics
```
......@@ -77,7 +77,7 @@ Example response:
List information about the jobs that Sidekiq has performed.
```
```plaintext
GET /sidekiq/job_stats
```
......@@ -102,7 +102,7 @@ Example response:
List all the currently available information about Sidekiq.
```
```plaintext
GET /sidekiq/compound_metrics
```
......
......@@ -8,7 +8,7 @@ administrator in order to perform this action.
NOTE: **Note:**
These statistics are approximate.
```
```plaintext
GET /application/statistics
```
......
......@@ -7,7 +7,7 @@ Every API call to suggestions must be authenticated.
Applies a suggested patch in a merge request. Users must be
at least [Developer](../user/permissions.md) to perform such action.
```
```plaintext
PUT /suggestions/:id/apply
```
......
......@@ -11,7 +11,7 @@ Read more about [system hooks](../system_hooks/system_hooks.md).
Get a list of all system hooks.
```
```plaintext
GET /hooks
```
......@@ -42,7 +42,7 @@ Example response:
Add a new system hook.
```
```plaintext
POST /hooks
```
......@@ -81,7 +81,7 @@ Example response:
## Test system hook
```
```plaintext
GET /hooks/:id
```
......@@ -112,7 +112,7 @@ Example response:
Deletes a system hook.
```
```plaintext
DELETE /hooks/:id
```
......
......@@ -6,7 +6,7 @@ Get a list of repository tags from a project, sorted by name in reverse
alphabetical order. This endpoint can be accessed without authentication if the
repository is publicly accessible.
```
```plaintext
GET /projects/:id/repository/tags
```
......@@ -57,7 +57,7 @@ Parameters:
Get a specific repository tag determined by its name. This endpoint can be
accessed without authentication if the repository is publicly accessible.
```
```plaintext
GET /projects/:id/repository/tags/:tag_name
```
......@@ -104,7 +104,7 @@ Example Response:
Creates a new tag in the repository that points to the supplied ref.
```
```plaintext
POST /projects/:id/repository/tags
```
......@@ -164,7 +164,7 @@ status code `405` with an explaining error message is returned.
Deletes a tag of a repository with given name.
```
```plaintext
DELETE /projects/:id/repository/tags/:tag_name
```
......@@ -178,7 +178,7 @@ Parameters:
Add release notes to the existing Git tag. If there
already exists a release for the given tag, status code `409` is returned.
```
```plaintext
POST /projects/:id/repository/tags/:tag_name/release
```
......@@ -210,7 +210,7 @@ Response:
Updates the release notes of a given release.
```
```plaintext
PUT /projects/:id/repository/tags/:tag_name/release
```
......
......@@ -12,7 +12,7 @@ information on Dockerfiles, see the
Get all Dockerfile templates.
```
```plaintext
GET /templates/dockerfiles
```
......@@ -99,7 +99,7 @@ Example response:
Get a single Dockerfile template.
```
```plaintext
GET /templates/dockerfiles/:key
```
......
......@@ -13,7 +13,7 @@ resources available online.
Get all license templates.
```
```plaintext
GET /templates/licenses
```
......@@ -110,7 +110,7 @@ Example response:
Get a single license template. You can pass parameters to replace the license
placeholder.
```
```plaintext
GET /templates/licenses/:key
```
......
......@@ -7,7 +7,7 @@
Returns a list of todos. When no filter is applied, it returns all pending todos
for the current user. Different filters allow the user to precise the request.
```
```plaintext
GET /todos
```
......@@ -184,7 +184,7 @@ Example Response:
Marks a single pending todo given by its ID for the current user as done. The
todo marked as done is returned in the response.
```
```plaintext
POST /todos/:id/mark_as_done
```
......@@ -280,7 +280,7 @@ Example Response:
Marks all pending todos for the current user as done. It returns the HTTP status code `204` with an empty response.
```
```plaintext
POST /todos/mark_as_done
```
......
This diff is collapsed.
......@@ -5,7 +5,7 @@
Retrieve version information for this GitLab instance. Responds `200 OK` for
authenticated users.
```
```plaintext
GET /version
```
......
......@@ -10,7 +10,7 @@ feedback from [Visual Reviews](../ci/review_apps/index.md#visual-reviews-starter
Creates a new thread to a single project merge request. This is similar to creating
a note but other comments (replies) can be added to it later.
```
```plaintext
POST /projects/:id/merge_requests/:merge_request_iid/visual_review_discussions
```
......
......@@ -34,7 +34,7 @@ Read more on [pagination](README.md#pagination).
List all of a project's vulnerability findings.
```
```plaintext
GET /projects/:id/vulnerability_findings
GET /projects/:id/vulnerability_findings?report_type=sast
GET /projects/:id/vulnerability_findings?report_type=container_scanning
......
......@@ -8,7 +8,7 @@ Available only in APIv4.
Get all wiki pages for a given project.
```
```plaintext
GET /projects/:id/wikis
```
......@@ -49,7 +49,7 @@ Example response:
Get a wiki page for a given project.
```
```plaintext
GET /projects/:id/wikis/:slug
```
......@@ -77,7 +77,7 @@ Example response:
Creates a new wiki page for the given repository with the given title, slug, and content.
```
```plaintext
POST /projects/:id/wikis
```
......@@ -107,7 +107,7 @@ Example response:
Updates an existing wiki page. At least one parameter is required to update the wiki page.
```
```plaintext
PUT /projects/:id/wikis/:slug
```
......@@ -138,7 +138,7 @@ Example response:
Deletes a wiki page with a given slug.
```
```plaintext
DELETE /projects/:id/wikis/:slug
```
......@@ -160,7 +160,7 @@ On success the HTTP status code is `204` and no JSON response is expected.
Uploads a file to the attachment folder inside the wiki's repository. The
attachment folder is the `uploads` folder.
```
```plaintext
POST /projects/:id/wikis/attachments
```
......
......@@ -206,10 +206,11 @@ templates](https://gitlab.com/gitlab-org/gitlab-foss/tree/master/lib/gitlab/ci/t
### Caching Node.js dependencies
Assuming your project is using [npm](https://www.npmjs.com/) or
[Yarn](https://classic.yarnpkg.com/en/) to install the Node.js dependencies, the
following example defines `cache` globally so that all jobs inherit it.
Node.js modules are installed in `node_modules/` and are cached per-branch:
Assuming your project is using [npm](https://www.npmjs.com/) to install the Node.js
dependencies, the following example defines `cache` globally so that all jobs inherit it.
By default, npm stores cache data in the home folder `~/.npm` but since
[you can't cache things outside of the project directory](../yaml/README.md#cachepaths),
we tell npm to use `./.npm` instead, and it is cached per-branch:
```yaml
#
......@@ -221,10 +222,10 @@ image: node:latest
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
- .npm/
before_script:
- npm install
- npm ci --cache .npm --prefer-offline
test_async:
script:
......
......@@ -571,9 +571,12 @@ Below you can find supported syntax reference:
- `$VARIABLE =~ /^content.*/`
- `$VARIABLE_1 !~ /^content.*/` (introduced in GitLab 11.11)
It is possible perform pattern matching against a variable and regular
expression. Expression like this evaluates to truth if matches are found
when using `=~`. It evaluates to truth if matches are not found when `!~` is used.
Variable pattern matching with regular expressions uses the
[RE2 regular expression syntax](https://github.com/google/re2/wiki/Syntax).
Expressions evaluate as `true` if:
- Matches are found when using `=~`.
- Matches are *not* found when using `!~`.
Pattern matching is case-sensitive by default. Use `i` flag modifier, like
`/pattern/i` to make a pattern case-insensitive.
......
......@@ -889,7 +889,10 @@ In this example, if the first rule:
`rules:if` differs slightly from `only:variables` by accepting only a single
expression string, rather than an array of them. Any set of expressions to be
evaluated should be conjoined into a single expression using `&&` or `||`. For example:
evaluated should be conjoined into a single expression using `&&` or `||`, and use
the [variable matching syntax](../variables/README.md#supported-syntax).
For example:
```yaml
job:
......
......@@ -9,8 +9,20 @@ description: 'Learn how to contribute to GitLab.'
- Set up GitLab's development environment with [GitLab Development Kit (GDK)](https://gitlab.com/gitlab-org/gitlab-development-kit/blob/master/doc/howto/README.md)
- [GitLab contributing guide](contributing/index.md)
- [Issues workflow](contributing/issue_workflow.md) (issue tracker guidelines, triaging, labels, feature proposals, issue weight, regression issues, technical and UX debt)
- [Merge requests workflow](contributing/merge_request_workflow.md) (merge request guidelines, contribution acceptance criteria, definition of done, dependencies)
- [Issues workflow](contributing/issue_workflow.md). For information on:
- Issue tracker guidelines.
- Triaging.
- Labels.
- Feature proposals.
- Issue weight.
- Regression issues.
- Technical or UX debt.
- [Merge requests workflow](contributing/merge_request_workflow.md). For
information on:
- Merge request guidelines.
- Contribution acceptance criteria.
- Definition of done.
- Dependencies.
- [Style guides](contributing/style_guides.md)
- [Implement design & UI elements](contributing/design.md)
- [GitLab Architecture Overview](architecture.md)
......@@ -85,6 +97,7 @@ Complementary reads:
- [Issue types vs first-class types](issue_types.md)
- [Application limits](application_limits.md)
- [Redis guidelines](redis.md)
- [Rails initializers](rails_initializers.md)
## Performance guides
......
......@@ -288,7 +288,7 @@ GitLab CI is the open-source continuous integration service included with GitLab
- Configuration: [Omnibus][grafana-omnibus], [Charts][grafana-charts]
- Layer: Monitoring
Grafana is an open source, feature rich metrics dashboard and graph editor for Graphite, Elasticsearch, OpenTSDB, Prometheus and InfluxDB.
Grafana is an open source, feature rich metrics dashboard and graph editor for Graphite, Elasticsearch, OpenTSDB, Prometheus, and InfluxDB.
#### Jaeger
......@@ -321,7 +321,7 @@ Mattermost is an open source, private cloud, Slack-alternative from <https://mat
- Configuration: [Omnibus][minio-omnibus], [Charts][minio-charts], [GDK][minio-gdk]
- Layer: Core Service (Data)
MinIO is an object storage server released under Apache License v2.0. It is compatible with Amazon S3 cloud storage service. It is best suited for storing unstructured data such as photos, videos, log files, backups and container / VM images. Size of an object can range from a few KBs to a maximum of 5TB.
MinIO is an object storage server released under Apache License v2.0. It is compatible with Amazon S3 cloud storage service. It is best suited for storing unstructured data such as photos, videos, log files, backups, and container / VM images. Size of an object can range from a few KBs to a maximum of 5TB.
#### NGINX
......
......@@ -17,7 +17,7 @@ uncovered edge cases. The reviewer can be from a different team, but it is
recommended to pick someone who knows the domain well. You can read more about the
importance of involving reviewer(s) in the section on the responsibility of the author below.
If you need some guidance (e.g. it's your first merge request), feel free to ask
If you need some guidance (for example, it's your first merge request), feel free to ask
one of the [Merge request coaches](https://about.gitlab.com/company/team/).
If you need assistance with security scans or comments, feel free to include the
......@@ -148,7 +148,7 @@ architecture, code organization, separation of concerns, tests, DRYness,
consistency, and readability.
Since a maintainer's job only depends on their knowledge of the overall GitLab
codebase, and not that of any specific domain, they can review, approve and merge
codebase, and not that of any specific domain, they can review, approve, and merge
merge requests from any team and in any product area.
In fact, authors are encouraged to get their merge requests merged by maintainers
......@@ -334,7 +334,7 @@ reviewee.
reviewer before doing it, but have the courage to do it when you believe it is
important.
- In the interest of [Iteration](https://about.gitlab.com/handbook/values/#iteration),
if, as a reviewer, your suggestions are non-blocking changes or personal preference
if your review suggestions are non-blocking changes, or personal preference
(not a documented or agreed requirement), consider approving the merge request
before passing it back to the author. This allows them to implement your suggestions
if they agree, or allows them to pass it onto the
......
......@@ -9,7 +9,11 @@ To better understand the priority by which UX tackles issues, see the [UX sectio
Once an issue has been worked on and is ready for development, a UXer removes the ~"UX" label and applies the ~"UX ready" label to that issue.
There is a special type label called ~"product discovery". It represents a discovery issue intended for UX, PM, FE, and BE to discuss the problem and potential solutions. The final output for this issue could be a doc of requirements, a design artifact, or even a prototype. The solution will be developed in a subsequent milestone.
There is a special type label called ~"product discovery" intended for UX,
PM, FE, and BE. It represents a discovery issue to discuss the problem and
potential solutions. The final output for this issue could be a doc of
requirements, a design artifact, or even a prototype. The solution will be
developed in a subsequent milestone.
~"product discovery" issues are like any other issue and should contain a milestone label, ~"Deliverable" or ~"Stretch", when scheduled in the current milestone.
......@@ -17,7 +21,7 @@ The initial issue should be about the problem we are solving. If a separate [pro
is needed for additional research and design work, it will be created by a PM or UX person.
Assign the ~UX, ~"product discovery" and ~"Deliverable" labels, add a milestone and
use a title that makes it clear that the scheduled issue is product discovery
(e.g. `Product discovery for XYZ`).
(for example, `Product discovery for XYZ`).
In order to complete a product discovery issue in a release, you must complete the following:
......
......@@ -12,7 +12,7 @@ A database review is required for:
including files in:
- `db/`
- `lib/gitlab/background_migration/`
- Changes to the database tooling, e.g.:
- Changes to the database tooling. For example:
- migration or ActiveRecord helpers in `lib/gitlab/database/`
- load balancing
- Changes that produce SQL queries that are beyond the obvious. It is
......@@ -50,7 +50,7 @@ A database **reviewer**'s role is to:
Currently we have a [critical shortage of database maintainers](https://gitlab.com/gitlab-org/gitlab/issues/29717). Until we are able to increase the number of database maintainers to support the volume of reviews, we have implemented this temporary solution. If the database **reviewer** cannot find an available database **maintainer** then:
1. Assign the MR for a second review by a **database trainee maintainer** for further review.
1. Once satisfied with the review process, and if the database **maintainer** is still not available, skip the database maintainer approval step and assign the merge request to a backend maintainer for final review and approval.
1. Once satisfied with the review process and if the database **maintainer** is still not available, skip the database maintainer approval step and assign the merge request to a backend maintainer for final review and approval.
A database **maintainer**'s role is to:
......@@ -119,10 +119,10 @@ the following preparations into account.
- Add foreign keys to any columns pointing to data in other tables, including [an index](migration_style_guide.md#adding-foreign-key-constraints).
- Add indexes for fields that are used in statements such as `WHERE`, `ORDER BY`, `GROUP BY`, and `JOIN`s.
#### Preparation when removing columns, tables, indexes or other structures
#### Preparation when removing columns, tables, indexes, or other structures
- Follow the [guidelines on dropping columns](what_requires_downtime.md#dropping-columns).
- Generally it's best practice, but not a hard rule, to remove indexes and foreign keys in a post-deployment migration.
- Generally it's best practice (but not a hard rule) to remove indexes and foreign keys in a post-deployment migration.
- Exceptions include removing indexes and foreign keys for small tables.
### How to review for database
......@@ -156,14 +156,14 @@ the following preparations into account.
- Check migrations are reversible and implement a `#down` method
- Check data migrations:
- Establish a time estimate for execution on GitLab.com.
- Depending on timing, data migrations can be placed on regular, post-deploy or background migrations.
- Depending on timing, data migrations can be placed on regular, post-deploy, or background migrations.
- Data migrations should be reversible too or come with a description of how to reverse, when possible.
This applies to all types of migrations (regular, post-deploy, background).
- Query performance
- Check for any obviously complex queries and queries the author specifically
points out for review (if any)
- If not present yet, ask the author to provide SQL queries and query plans
(e.g. by using [chatops](understanding_explain_plans.md#chatops) or direct
(for example, by using [chatops](understanding_explain_plans.md#chatops) or direct
database access)
- For given queries, review parameters regarding data distribution
- [Check query plans](understanding_explain_plans.md) and suggest improvements
......@@ -187,4 +187,4 @@ NOTE: **Note:** Keep in mind that all runtimes should be measured against GitLab
|----|----|---|
| Regular migrations on `db/migrate` | `3 minutes` | A valid exception are index creation as this can take a long time. |
| Post migrations on `db/post_migrate` | `10 minutes` | |
| Background migrations | --- | Since these are suitable for larger tables, it's not possible to set a precise timing guideline, however, any query must stay well below `10s` of execution time. |
| Background migrations | --- | Since these are suitable for larger tables, it's not possible to set a precise timing guideline, however, any single query must stay below `1 second` execution time with cold caches. |
......@@ -16,7 +16,7 @@ In addition to this page, the following resources can help you craft and contrib
## Source files and rendered web locations
Documentation for GitLab, GitLab Runner, Omnibus GitLab and Charts is published to <https://docs.gitlab.com>. Documentation for GitLab is also published within the application at `/help` on the domain of the GitLab instance.
Documentation for GitLab, GitLab Runner, Omnibus GitLab, and Charts is published to <https://docs.gitlab.com>. Documentation for GitLab is also published within the application at `/help` on the domain of the GitLab instance.
At `/help`, only help for your current edition and version is included. Help for other versions is available at <https://docs.gitlab.com/archives/>.
The source of the documentation exists within the codebase of each GitLab application in the following repository locations:
......
......@@ -107,7 +107,7 @@ The pipeline in the `gitlab-docs` project:
### Rebuild the docs site Docker images
Once a week, on Mondays, a scheduled pipeline runs and rebuilds the Docker images
Once a week on Mondays, a scheduled pipeline runs and rebuilds the Docker images
used in various pipeline jobs, like `docs-lint`. The Docker image configuration files are
located at <https://gitlab.com/gitlab-org/gitlab-docs/-/tree/master/dockerfiles>.
......@@ -230,7 +230,7 @@ for its search function. This is how it works:
NOTE: **For GitLab employees:**
The credentials to access the Algolia dashboard are stored in 1Password. If you
want to receive weekly reports of the search usage, search the Google doc with
title "Email, Slack, and GitLab Groups and Aliases", search for `docsearch`,
title `Email, Slack, and GitLab Groups and Aliases`, search for `docsearch`,
and add a comment with your email to be added to the alias that gets the weekly
reports.
......
......@@ -17,14 +17,12 @@ that apply to all GitLab content, not just documentation.
### Why a single source of truth
The documentation is the SSOT for all information related to the implementation, usage, and troubleshooting of GitLab products and features. It evolves continually, in keeping with new products and features, and with improvements for clarity, accuracy, and completeness.
The documentation of GitLab products and features is the SSOT for all information related to implementation, usage, and troubleshooting. It evolves continually, in keeping with new products and features, and with improvements for clarity, accuracy, and completeness.
This policy prevents information silos, ensuring that it remains easy to find information about GitLab products.
It also informs decisions about the kinds of content we include in our documentation.
The documentation is a continually evolving SSOT for all information related to the implementation, usage, and troubleshooting of GitLab products and features.
### All information
Include problem-solving actions that may address rare cases or be considered 'risky', so long as proper context is provided in the form of fully detailed warnings and caveats. This kind of content should be included as it could be helpful to others and, when properly explained, its benefits outweigh the risks. If you think you have found an exception to this rule, contact the Technical Writing team.
......@@ -34,7 +32,7 @@ For the Troubleshooting sections, people in GitLab Support can merge additions t
### All media types
Include any media types/sources if the content is relevant to readers. You can freely include or link presentations, diagrams, videos, etc.; no matter who it was originally composed for, if it is helpful to any of our audiences, we can include it.
Include any media types/sources if the content is relevant to readers. You can freely include or link presentations, diagrams, videos, and so on; no matter who it was originally composed for, if it is helpful to any of our audiences, we can include it.
- If you use an image that has a separate source file (for example, a vector or diagram format), link the image to the source file so that it may be reused or updated by anyone.
- Do not copy and paste content from other sources unless it is a limited quotation with the source cited. Typically it is better to either rephrase relevant information in your own words or link out to the other source.
......@@ -63,13 +61,17 @@ Instead, link to the SSOT and explain why it is important to consume the informa
### Organize by topic, not by type
Beyond top-level audience-type folders (e.g. `administration`), we organize content by topic, not by type, so that it can be located as easily as possible within the single-source-of-truth (SSOT) section for the subject matter.
Beyond top-level audience-type folders (for example, `administration`), we organize content by topic, not by type, so that it can be located as easily as possible within the single-source-of-truth (SSOT) section for the subject matter.
For example, do not create groupings of similar media types. For example:
For example, do not create groupings of similar media types (e.g. glossaries, FAQs, or sets of all articles or videos).
- Glossaries.
- FAQs.
- Sets of all articles or videos.
Such grouping of content by type makes
it difficult to browse for the information you need and difficult to maintain up-to-date content.
Instead, organize content by its subject (e.g. everything related to CI goes together)
Instead, organize content by its subject (for example, everything related to CI goes together)
and cross-link between any related content.
### Docs-first methodology
......@@ -79,7 +81,10 @@ We employ a **docs-first methodology** to help ensure that the docs remain a com
- If the answer to a question exists in documentation, share the link to the docs instead of rephrasing the information.
- When you encounter new information not available in GitLab’s documentation (for example, when working on a support case or testing a feature), your first step should be to create a merge request (MR) to add this information to the docs. You can then share the MR in order to communicate this information.
New information that would be useful toward the future usage or troubleshooting of GitLab should not be written directly in a forum or other messaging system, but added to a docs MR and then referenced, as described above. Note that among any other doc changes, you can always add a Troubleshooting section to a doc if none exists, or un-comment and use the placeholder Troubleshooting section included as part of our [doc template](structure.md#template-for-new-docs), if present.
New information that would be useful toward the future usage or troubleshooting of GitLab should not be written directly in a forum or other messaging system, but added to a docs MR and then referenced, as described above. Note that among any other doc changes, you can either:
- Add a Troubleshooting section to a doc if none exists.
- Un-comment and use the placeholder Troubleshooting section included as part of our [doc template](structure.md#template-for-new-docs), if present.
The more we reflexively add useful information to the docs, the more (and more successfully) the docs will be used to efficiently accomplish tasks and solve problems.
......@@ -98,7 +103,7 @@ Ruby gem will support all [GFM markup](../../user/markdown.md) in the future. Th
all markup that is supported for display in the GitLab application itself. For now,
use regular Markdown markup, following the rules in the linked style guide.
Note that Kramdown-specific markup (e.g., `{:.class}`) will not render properly on GitLab instances under [`/help`](index.md#gitlab-help).
Note that Kramdown-specific markup (for example, `{:.class}`) will not render properly on GitLab instances under [`/help`](index.md#gitlab-help).
Hard-coded HTML is valid, although it's discouraged to be used while we have `/help`. HTML is permitted as long as:
......@@ -1149,7 +1154,7 @@ keyword "only":
- For GitLab Premium: `**(PREMIUM ONLY)**`.
- For GitLab Ultimate: `**(ULTIMATE ONLY)**`.
For GitLab.com only tiers (when the feature is not available for self-hosted instances):
For GitLab.com only tiers (when the feature is not available for self-managed instances):
- For GitLab Free and higher tiers: `**(FREE ONLY)**`.
- For GitLab Bronze and higher tiers: `**(BRONZE ONLY)**`.
......
......@@ -118,7 +118,7 @@ Reviewers help ensure:
Prior to merging, documentation changes committed by the developer must be reviewed by:
- The code reviewer for the merge request. This is known as a technical review.
- Optionally, others involved in the work, such as other developers or the Product Manager.
- Optionally, others involved in the work such as other developers or the Product Manager.
- The Technical Writer for the DevOps stage group, except in exceptional circumstances where a
[post-merge review](#post-merge-reviews) can be requested.
- A maintainer of the project.
......@@ -137,11 +137,11 @@ For issues requiring any new or updated documentation, the Product Manager must:
- Confirm or add the [documentation requirements](#documentation-requirements).
- Ensure the issue contains:
- Any new or updated feature name.
- Overview, description, and use cases, as required by the
[documentation structure and template](structure.md), when applicable.
- Overview, description, and use cases when applicable (as required by the
[documentation structure and template](structure.md)).
Everyone is encouraged to draft the documentation requirements in the issue, but a Product Manager
will do the following:
Everyone is encouraged to draft the documentation requirements in the issue. However, a Product
Manager will:
- When the issue is assigned a release milestone, review and update the Documentation details.
- By the kickoff, finalize the documentation details.
......@@ -238,7 +238,7 @@ The following details should be included:
- What concepts and procedures should the documentation guide and enable the user to understand or
accomplish?
- To this end, what new page(s) are needed, if any? What pages or subsections need updates?
Consider user, admin, and API documentation changes and additions.
Consider changes and additions to user, admin, and API documentation.
- For any guide or instruction set, should it help address a single use case, or be flexible to
address a certain range of use cases?
- Do we need to update a previously recommended workflow? Should we link the new feature from
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment