Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2022-10-27 00:10:35 +00:00
parent f825fd1d88
commit 135dd0646b
129 changed files with 3816 additions and 3966 deletions

View File

@ -54,16 +54,6 @@ Gitlab/Json:
- 'app/models/merge_request_diff_commit.rb'
- 'app/presenters/packages/composer/packages_presenter.rb'
- 'app/presenters/projects/security/configuration_presenter.rb'
- 'app/services/bulk_imports/lfs_objects_export_service.rb'
- 'app/services/ci/pipeline_artifacts/coverage_report_service.rb'
- 'app/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service.rb'
- 'app/services/ci/pipeline_trigger_service.rb'
- 'app/services/ci/register_job_service.rb'
- 'app/services/google_cloud/create_service_accounts_service.rb'
- 'app/services/google_cloud/setup_cloudsql_instance_service.rb'
- 'app/services/merge_requests/add_context_service.rb'
- 'app/services/projects/lfs_pointers/lfs_download_link_list_service.rb'
- 'app/services/service_ping/submit_service.rb'
- 'app/workers/google_cloud/create_cloudsql_instance_worker.rb'
- 'config/initializers/rack_multipart_patch.rb'
- 'db/migrate/20210305031822_create_dast_site_profile_variables.rb'

View File

@ -176,7 +176,7 @@ gem 'typhoeus', '~> 1.4.0' # Used with Elasticsearch to support http keep-alive
# Markdown and HTML processing
gem 'html-pipeline', '~> 2.13.2'
gem 'deckar01-task_list', '2.3.1'
gem 'deckar01-task_list', '2.3.2'
gem 'gitlab-markup', '~> 1.8.0'
gem 'github-markup', '~> 1.7.0', require: 'github/markup'
gem 'commonmarker', '~> 0.23.6'

View File

@ -97,7 +97,7 @@
{"name":"danger-gitlab","version":"8.0.0","platform":"ruby","checksum":"497dd7d0f6513913de651019223d8058cf494df10acbd17de92b175dfa04a3a8"},
{"name":"database_cleaner","version":"1.7.0","platform":"ruby","checksum":"bdf833c197afac7054015bcde2567c3834c366bbfe6a377c30151ca984b32016"},
{"name":"dead_end","version":"3.1.1","platform":"ruby","checksum":"1011df7f7c0149be004e11cbbc37747760227c55305cd902fd3c06e1394b2f5b"},
{"name":"deckar01-task_list","version":"2.3.1","platform":"ruby","checksum":"205a08c22a8f05c063d43c452aeada4214b58fdce1e55e92ec709e2cfb9e7ca1"},
{"name":"deckar01-task_list","version":"2.3.2","platform":"ruby","checksum":"5a19092548d24309d8b2c2704d64cdc08a4a615823c9a722f4142edec1de8805"},
{"name":"declarative","version":"0.0.20","platform":"ruby","checksum":"8021dd6cb17ab2b61233c56903d3f5a259c5cf43c80ff332d447d395b17d9ff9"},
{"name":"declarative-option","version":"0.1.0","platform":"ruby","checksum":"17508349f51c5631e5ad4158c29f78a4b2de618abffa066d76c11953705f91bc"},
{"name":"declarative_policy","version":"1.1.0","platform":"ruby","checksum":"9af4cf299ade03f2bbf63908f2ce6a117d132fc714c39a128596667fb13331cb"},

View File

@ -321,7 +321,7 @@ GEM
gitlab (~> 4.2, >= 4.2.0)
database_cleaner (1.7.0)
dead_end (3.1.1)
deckar01-task_list (2.3.1)
deckar01-task_list (2.3.2)
html-pipeline
declarative (0.0.20)
declarative-option (0.1.0)
@ -1584,7 +1584,7 @@ DEPENDENCIES
crystalball (~> 0.7.0)
cvss-suite (~> 3.0.1)
database_cleaner (~> 1.7.0)
deckar01-task_list (= 2.3.1)
deckar01-task_list (= 2.3.2)
declarative_policy (~> 1.1.0)
default_value_for (~> 3.4.0)
deprecation_toolkit (~> 1.5.1)

View File

@ -233,13 +233,7 @@ export default {
:description="$options.translations.addTokenNameDescription"
label-for="deploy_token_name"
>
<gl-form-input
id="deploy_token_name"
v-model="name"
name="deploy_token_name"
class="qa-deploy-token-name"
data-qa-selector="deploy_token_name_field"
/>
<gl-form-input id="deploy_token_name" v-model="name" name="deploy_token_name" />
</gl-form-group>
<gl-form-group
:label="$options.translations.addTokenExpiryLabel"

View File

@ -39,7 +39,7 @@ export function initStoreFromElement(element) {
export function initPropsFromElement(element) {
return {
providerTitle: element.dataset.provider,
providerTitle: element.dataset.providerTitle,
filterable: parseBoolean(element.dataset.filterable),
paginatable: parseBoolean(element.dataset.paginatable),
optionalStages: JSON.parse(element.dataset.optionalStages),

View File

@ -169,7 +169,8 @@ export default class LazyLoader {
delete img.dataset.src;
img.classList.remove('lazy');
img.classList.add('js-lazy-loaded');
img.classList.add('qa-js-lazy-loaded');
// eslint-disable-next-line no-param-reassign
img.dataset.qa_selector = 'js_lazy_loaded_content';
}
}
}

View File

@ -6,7 +6,7 @@ module Types
class Runner < BasePermissionType
graphql_name 'RunnerPermissions'
abilities :read_runner, :update_runner, :delete_runner
abilities :read_runner, :update_runner, :delete_runner, :assign_runner
end
end
end

View File

@ -70,10 +70,11 @@ module IconsHelper
# gl_loading_icon(css_class: "foo-bar")
#
# See also https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-loading-icon--default
def gl_loading_icon(inline: false, color: 'dark', size: 'sm', css_class: nil)
def gl_loading_icon(inline: false, color: 'dark', size: 'sm', css_class: nil, data: nil)
spinner = content_tag(:span, "", {
class: %[gl-spinner gl-spinner-#{color} gl-spinner-#{size} gl-vertical-align-text-bottom!],
aria: { label: _('Loading') }
aria: { label: _('Loading') },
data: data
})
container_classes = ['gl-spinner-container']

View File

@ -71,20 +71,20 @@ class Integration < ApplicationRecord
alias_attribute :type, :type_new
default_value_for :active, false
default_value_for :alert_events, true
default_value_for :category, 'common'
default_value_for :commit_events, true
default_value_for :confidential_issues_events, true
default_value_for :confidential_note_events, true
default_value_for :issues_events, true
default_value_for :job_events, true
default_value_for :merge_requests_events, true
default_value_for :note_events, true
default_value_for :pipeline_events, true
default_value_for :push_events, true
default_value_for :tag_push_events, true
default_value_for :wiki_page_events, true
attribute :active, default: false
attribute :alert_events, default: true
attribute :category, default: 'common'
attribute :commit_events, default: true
attribute :confidential_issues_events, default: true
attribute :confidential_note_events, default: true
attribute :issues_events, default: true
attribute :job_events, default: true
attribute :merge_requests_events, default: true
attribute :note_events, default: true
attribute :pipeline_events, default: true
attribute :push_events, default: true
attribute :tag_push_events, default: true
attribute :wiki_page_events, default: true
after_initialize :initialize_properties

View File

@ -60,7 +60,7 @@ module BulkImports
def write_lfs_json
filepath = File.join(export_path, "#{BulkImports::FileTransfer::ProjectConfig::LFS_OBJECTS_RELATION}.json")
File.write(filepath, lfs_json.to_json)
File.write(filepath, Gitlab::Json.dump(lfs_json))
end
end
end

View File

@ -39,7 +39,7 @@ module Ci
def carrierwave_file
strong_memoize(:carrier_wave_file) do
CarrierWaveStringFile.new_file(
file_content: report.to_json,
file_content: Gitlab::Json.dump(report),
filename: Ci::PipelineArtifact::DEFAULT_FILE_NAMES.fetch(:code_coverage),
content_type: 'application/json'
)

View File

@ -77,9 +77,9 @@ module Ci
end
def build_quality_mr_diff_report(mr_diff_report)
mr_diff_report.each_with_object({}) do |diff_report, hash|
Gitlab::Json.dump(mr_diff_report.each_with_object({}) do |diff_report, hash|
hash[diff_report.first] = Ci::CodequalityMrDiffReportSerializer.new.represent(diff_report.second) # rubocop: disable CodeReuse/Serializer
end.to_json
end)
end
end
end

View File

@ -93,7 +93,7 @@ module Ci
def payload_variable
{ key: PAYLOAD_VARIABLE_KEY,
value: params.except(*PAYLOAD_VARIABLE_HIDDEN_PARAMS).to_json,
value: Gitlab::Json.dump(params.except(*PAYLOAD_VARIABLE_HIDDEN_PARAMS)),
variable_type: :file }
end

View File

@ -226,7 +226,7 @@ module Ci
log_artifacts_context(build)
log_build_dependencies_size(presented_build)
build_json = ::API::Entities::Ci::JobRequest::Response.new(presented_build).to_json
build_json = Gitlab::Json.dump(::API::Entities::Ci::JobRequest::Response.new(presented_build))
Result.new(build, build_json, true)
end

View File

@ -1,112 +0,0 @@
# frozen_string_literal: true
require 'openssl'
module Clusters
module Kubernetes
class ConfigureIstioIngressService
PASSTHROUGH_RESOURCE = Kubeclient::Resource.new(
mode: 'PASSTHROUGH'
).freeze
MTLS_RESOURCE = Kubeclient::Resource.new(
mode: 'MUTUAL',
privateKey: '/etc/istio/ingressgateway-certs/tls.key',
serverCertificate: '/etc/istio/ingressgateway-certs/tls.crt',
caCertificates: '/etc/istio/ingressgateway-ca-certs/cert.pem'
).freeze
def initialize(cluster:)
@cluster = cluster
@platform = cluster.platform
@kubeclient = platform.kubeclient
@knative = cluster.application_knative
end
def execute
return configure_certificates if serverless_domain_cluster
configure_passthrough
rescue Kubeclient::HttpError => e
knative.make_errored!(_('Kubernetes error: %{error_code}') % { error_code: e.error_code })
rescue StandardError
knative.make_errored!(_('Failed to update.'))
end
private
attr_reader :cluster, :platform, :kubeclient, :knative
def serverless_domain_cluster
knative&.serverless_domain_cluster
end
def configure_certificates
create_or_update_istio_cert_and_key
set_gateway_wildcard_https(MTLS_RESOURCE)
end
def create_or_update_istio_cert_and_key
name = OpenSSL::X509::Name.parse("CN=#{knative.hostname}")
key = OpenSSL::PKey::RSA.new(2048)
cert = OpenSSL::X509::Certificate.new
cert.version = 2
cert.serial = 0
cert.not_before = Time.current
cert.not_after = Time.current + 1000.years
cert.public_key = key.public_key
cert.subject = name
cert.issuer = name
cert.sign(key, OpenSSL::Digest.new('SHA256'))
serverless_domain_cluster.update!(
key: key.to_pem,
certificate: cert.to_pem
)
kubeclient.create_or_update_secret(istio_ca_certs_resource)
kubeclient.create_or_update_secret(istio_certs_resource)
end
def istio_ca_certs_resource
Gitlab::Kubernetes::GenericSecret.new(
'istio-ingressgateway-ca-certs',
{
'cert.pem': Base64.strict_encode64(serverless_domain_cluster.certificate)
},
Clusters::Kubernetes::ISTIO_SYSTEM_NAMESPACE
).generate
end
def istio_certs_resource
Gitlab::Kubernetes::TlsSecret.new(
'istio-ingressgateway-certs',
serverless_domain_cluster.certificate,
serverless_domain_cluster.key,
Clusters::Kubernetes::ISTIO_SYSTEM_NAMESPACE
).generate
end
def set_gateway_wildcard_https(tls_resource)
gateway_resource = gateway
gateway_resource.spec.servers.each do |server|
next unless server.hosts == ['*'] && server.port.name == 'https'
server.tls = tls_resource
end
kubeclient.update_gateway(gateway_resource)
end
def configure_passthrough
set_gateway_wildcard_https(PASSTHROUGH_RESOURCE)
end
def gateway
kubeclient.get_gateway('knative-ingress-gateway', Clusters::Kubernetes::KNATIVE_SERVING_NAMESPACE)
end
end
end
end

View File

@ -53,11 +53,11 @@ module Git
def create_pipelines
return unless params.fetch(:create_pipelines, true)
Ci::CreatePipelineService
.new(project, current_user, pipeline_params)
.execute!(:push, pipeline_options)
rescue Ci::CreatePipelineService::CreateError => ex
log_pipeline_errors(ex)
if ::Feature.enabled?(:refactored_create_pipeline_execution_method, project)
create_pipeline_refactored
else
create_pipeline_legacy
end
end
def execute_project_hooks
@ -148,14 +148,14 @@ module Git
{}
end
def log_pipeline_errors(exception)
def log_pipeline_errors(error_message)
data = {
class: self.class.name,
correlation_id: Labkit::Correlation::CorrelationId.current_id.to_s,
project_id: project.id,
project_path: project.full_path,
message: "Error creating pipeline",
errors: exception.to_s,
errors: error_message,
pipeline_params: sanitized_pipeline_params
}
@ -175,5 +175,21 @@ module Git
Gitlab::IntegrationsLogger
end
end
def create_pipeline_refactored
response = Ci::CreatePipelineService
.new(project, current_user, pipeline_params)
.execute(:push, **pipeline_options)
log_pipeline_errors(response.message) unless response.payload.persisted?
end
def create_pipeline_legacy
Ci::CreatePipelineService
.new(project, current_user, pipeline_params)
.execute!(:push, pipeline_options)
rescue Ci::CreatePipelineService::CreateError => ex
log_pipeline_errors(ex.to_s) # Stringifying the ex here as I removed stringifying in log_pipeline_errors.
end
end
end

View File

@ -10,8 +10,8 @@ module GoogleCloud
service_accounts_service.add_for_project(
environment_name,
service_account.project_id,
service_account.to_json,
service_account_key.to_json,
Gitlab::Json.dump(service_account),
Gitlab::Json.dump(service_account_key),
ProtectedBranch.protected?(project, environment_name) || ProtectedTag.protected?(project, environment_name)
)

View File

@ -13,7 +13,7 @@ module GoogleCloud
get_instance_response = google_api_client.get_cloudsql_instance(gcp_project_id, instance_name)
if get_instance_response.state != INSTANCE_STATE_RUNNABLE
return error("CloudSQL instance not RUNNABLE: #{get_instance_response.to_json}")
return error("CloudSQL instance not RUNNABLE: #{Gitlab::Json.dump(get_instance_response)}")
end
save_instance_ci_vars(get_instance_response)
@ -42,7 +42,7 @@ module GoogleCloud
success
rescue Google::Apis::Error => err
error(message: err.to_json)
error(message: Gitlab::Json.dump(err))
end
private
@ -97,7 +97,7 @@ module GoogleCloud
database_response = google_api_client.create_cloudsql_database(gcp_project_id, instance_name, database_name)
if database_response.status != OPERATION_STATE_DONE
return error("Database creation failed: #{database_response.to_json}")
return error("Database creation failed: #{Gitlab::Json.dump(database_response)}")
end
success
@ -109,7 +109,7 @@ module GoogleCloud
user_response = google_api_client.create_cloudsql_user(gcp_project_id, instance_name, username, password)
if user_response.status != OPERATION_STATE_DONE
return error("User creation failed: #{user_response.to_json}")
return error("User creation failed: #{Gitlab::Json.dump(user_response)}")
end
success

View File

@ -65,7 +65,7 @@ module MergeRequests
sha: sha,
authored_date: Gitlab::Database.sanitize_timestamp(commit_hash[:authored_date]),
committed_date: Gitlab::Database.sanitize_timestamp(commit_hash[:committed_date]),
trailers: commit_hash.fetch(:trailers, {}).to_json
trailers: Gitlab::Json.dump(commit_hash.fetch(:trailers, {}))
)
end
end

View File

@ -95,10 +95,12 @@ module Projects
end
def request_body(oids)
{
body = {
operation: DOWNLOAD_ACTION,
objects: oids.map { |oid, size| { oid: oid, size: size } }
}.to_json
}
Gitlab::Json.dump(body)
end
def headers

View File

@ -63,7 +63,7 @@ module ServicePing
def submit_payload(payload, path: USAGE_DATA_PATH)
Gitlab::HTTP.post(
URI.join(base_url, path),
body: payload.to_json,
body: Gitlab::Json.dump(payload),
allow_local_requests: true,
headers: { 'Content-type' => 'application/json' }
)

View File

@ -4,7 +4,7 @@
- filterable = local_assigns.fetch(:filterable, true)
- paginatable = local_assigns.fetch(:paginatable, false)
- default_namespace_path = (local_assigns[:default_namespace] || current_user.namespace).full_path
- provider_title = Gitlab::ImportSources.title(provider)
- provider_title = Gitlab::ImportSources.title(local_assigns.fetch(:provider))
- optional_stages = local_assigns.fetch(:optional_stages, [])
- header_title _("New project"), new_project_path

View File

@ -12,6 +12,7 @@
img.src = imgUrl;
img.removeAttribute('data-src');
img.classList.remove('lazy');
img.classList.add('js-lazy-loaded', 'qa-js-lazy-loaded');
img.classList.add('js-lazy-loaded');
img.dataset.qa_selector = 'js_lazy_loaded_content';
});
}

View File

@ -5,12 +5,12 @@
.form-group
= f.label :key, s_('Profiles|Key'), class: 'label-bold'
= f.text_area :key, class: "form-control gl-form-input js-add-ssh-key-validation-input qa-key-public-key-field", rows: 8, required: true, data: { supported_algorithms: Gitlab::SSHPublicKey.supported_algorithms }
= f.text_area :key, class: "form-control gl-form-input js-add-ssh-key-validation-input", rows: 8, required: true, data: { supported_algorithms: Gitlab::SSHPublicKey.supported_algorithms, qa_selector: 'key_public_key_field' }
%p.form-text.text-muted= s_('Profiles|Begins with %{ssh_key_algorithms}.') % { ssh_key_algorithms: ssh_key_allowed_algorithms }
.form-row
.col.form-group
= f.label :title, s_('Profiles|Title'), class: 'label-bold'
= f.text_field :title, class: "form-control gl-form-input input-lg qa-key-title-field", required: true, placeholder: s_('Profiles|Example: MacBook key')
= f.text_field :title, class: "form-control gl-form-input input-lg", required: true, placeholder: s_('Profiles|Example: MacBook key'), data: { qa_selector: 'key_title_field' }
%p.form-text.text-muted= s_('Profiles|Key titles are publicly visible.')
.form-row
@ -29,4 +29,4 @@
button_options: { class: 'js-add-ssh-key-validation-confirm-submit' }) do
= _("Yes, add it")
.gl-mt-3
= f.submit s_('Profiles|Add key'), class: "js-add-ssh-key-validation-original-submit qa-add-key-button", pajamas_button: true
= f.submit s_('Profiles|Add key'), class: "js-add-ssh-key-validation-original-submit", pajamas_button: true, data: { qa_selector: 'add_key_button' }

View File

@ -16,5 +16,5 @@
- if create_mr_button_from_event?(event)
= c.actions do
= render Pajamas::ButtonComponent.new(variant: :confirm, href: create_mr_path_from_push_event(event), button_options: { class: 'qa-create-merge-request' }) do
= render Pajamas::ButtonComponent.new(variant: :confirm, href: create_mr_path_from_push_event(event), button_options: { data: { qa_selector: 'create_merge_request_button' }}) do
= _('Create merge request')

View File

@ -16,7 +16,7 @@
- if current_action?(:new) || current_action?(:create)
%span.float-left.gl-mr-3
\/
= text_field_tag 'file_name', params[:file_name], placeholder: "File name",
= text_field_tag 'file_name', params[:file_name], placeholder: "File name", data: { qa_selector: 'file_name_field' },
required: true, class: 'form-control gl-form-input new-file-name js-file-path-name-input', value: params[:file_name] || (should_suggest_gitlab_ci_yml? ? '.gitlab-ci.yml' : '')
= render 'template_selectors'
- if should_suggest_gitlab_ci_yml?
@ -38,7 +38,7 @@
.file-editor.code
- if Feature.enabled?(:source_editor_toolbar, current_user)
#editor-toolbar
.js-edit-mode-pane.qa-editor#editor{ data: { 'editor-loading': true, qa_selector: 'source_editor_preview_container' } }<
.js-edit-mode-pane#editor{ data: { 'editor-loading': true, qa_selector: 'source_editor_preview_container' } }<
%pre.editor-loading-content= params[:content] || local_assigns[:blob_data]
- if local_assigns[:path]
.js-edit-mode-pane#preview.hide

View File

@ -1 +1 @@
= gl_loading_icon(size: "md", css_class: "qa-spinner gl-my-4")
= gl_loading_icon(size: "md", css_class: "gl-my-4", data: { qa_selector: 'spinner_placeholder' })

View File

@ -1,19 +1,19 @@
- merged = local_assigns.fetch(:merged, false)
- commit = @repository.commit(branch.dereferenced_target)
- merge_project = merge_request_source_project_for_project(@project)
%li{ class: "branch-item gl-display-flex! gl-align-items-center! js-branch-item js-branch-#{branch.name}", data: { name: branch.name } }
%li{ class: "branch-item gl-display-flex! gl-align-items-center! js-branch-item js-branch-#{branch.name}", data: { name: branch.name, qa_selector: 'branch_container', qa_name: branch.name } }
.branch-info
.gl-display-flex.gl-align-items-center
= sprite_icon('branch', size: 12, css_class: 'gl-flex-shrink-0')
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name gl-ml-3 qa-branch-name' do
= link_to project_tree_path(@project, branch.name), class: 'item-title str-truncated-100 ref-name gl-ml-3', data: { qa_selector: 'branch_link' } do
= branch.name
= clipboard_button(text: branch.name, title: _("Copy branch name"))
- if branch.name == @repository.root_ref
= gl_badge_tag s_('DefaultBranchLabel|default'), { variant: :info, size: :sm }, { class: 'gl-ml-2' }
= gl_badge_tag s_('DefaultBranchLabel|default'), { variant: :info, size: :sm }, { class: 'gl-ml-2', data: { qa_selector: 'badge_content' } }
- elsif merged
= gl_badge_tag s_('Branches|merged'), { variant: :info, size: :sm }, { class: 'gl-ml-2', title: s_('Branches|Merged into %{default_branch}') % { default_branch: @repository.root_ref }, data: { toggle: 'tooltip', container: 'body' } }
= gl_badge_tag s_('Branches|merged'), { variant: :info, size: :sm }, { class: 'gl-ml-2', title: s_('Branches|Merged into %{default_branch}') % { default_branch: @repository.root_ref }, data: { toggle: 'tooltip', container: 'body', qa_selector: 'badge_content' } }
- if protected_branch?(@project, branch)
= gl_badge_tag s_('Branches|protected'), { variant: :success, size: :sm }, { class: 'gl-ml-2' }
= gl_badge_tag s_('Branches|protected'), { variant: :success, size: :sm }, { class: 'gl-ml-2', data: { qa_selector: 'badge_content' } }
= render_if_exists 'projects/branches/diverged_from_upstream', branch: branch

View File

@ -11,7 +11,7 @@
- c.header do
= panel_title
- c.body do
%ul.content-list.all-branches.qa-all-branches
%ul.content-list.all-branches{ data: { qa_selector: 'all_branches_container' } }
- branches.first(overview_max_branches).each do |branch|
= render "projects/branches/branch", branch: branch, merged: project.repository.merged_to_root_ref?(branch), commit_status: @branch_pipeline_statuses[branch.name], show_commit_status: @branch_pipeline_statuses.any?
- if branches.size > overview_max_branches

View File

@ -14,13 +14,14 @@
- if can? current_user, :push_code, @project
= link_to project_merged_branches_path(@project),
class: 'gl-button btn btn-danger btn-danger-secondary has-tooltip qa-delete-merged-branches',
class: 'gl-button btn btn-danger btn-danger-secondary has-tooltip',
title: s_("Branches|Delete all branches that are merged into '%{default_branch}'") % { default_branch: @project.repository.root_ref },
method: :delete,
aria: { label: s_('Branches|Delete merged branches') },
data: { confirm: s_('Branches|Deleting the merged branches cannot be undone. Are you sure?'),
confirm_btn_variant: 'danger',
container: 'body' } do
container: 'body',
qa_selector: 'delete_merged_branches_link' } do
= s_('Branches|Delete merged branches')
= link_to new_project_branch_path(@project), class: 'gl-button btn btn-confirm' do
= s_('Branches|New branch')

View File

@ -20,7 +20,7 @@
%p
= _('You can get started by cloning the repository or start adding files to it with one of the following options.')
.project-buttons.qa-quick-actions
.project-buttons{ data: { qa_selector: 'quick_actions_container' } }
.project-clone-holder.d-block.d-md-none.gl-mt-3.gl-mr-3
= render "shared/mobile_clone_panel"

View File

@ -5,10 +5,10 @@
= f.label :auth_method, _('Authentication method'), class: 'label-bold'
= f.select :auth_method,
options_for_select(auth_options, mirror.auth_method),
{}, { class: "custom-select gl-form-select js-mirror-auth-type qa-authentication-method" }
{}, { class: "custom-select gl-form-select js-mirror-auth-type", data: { qa_selector: 'authentication_method_field' } }
= f.hidden_field :auth_method, value: "password", class: "js-hidden-mirror-auth-type"
.form-group
.well-password-auth.collapse.js-well-password-auth
= f.label :password, _("Password"), class: "label-bold"
= f.password_field :password, class: 'form-control gl-form-input qa-password js-mirror-password-field', autocomplete: 'off'
= f.password_field :password, class: 'form-control gl-form-input js-mirror-password-field', autocomplete: 'off', data: { qa_selector: 'password_field' }

View File

@ -1,2 +1,2 @@
%span.qa-disabled-mirror-badge.rspec-disabled-mirror-badge{ data: { toggle: 'tooltip', html: 'true' }, title: _('Disabled mirrors can only be enabled by instance owners. It is recommended that you delete them.') }
%span.rspec-disabled-mirror-badge{ data: { toggle: 'tooltip', html: 'true' }, title: _('Disabled mirrors can only be enabled by instance owners. It is recommended that you delete them.') }
= gl_badge_tag _('Disabled'), variant: :warning

View File

@ -21,7 +21,7 @@
.form-group.has-feedback
= label_tag :url, _('Git repository URL'), class: 'label-light'
= text_field_tag :url, nil, class: 'form-control gl-form-input js-mirror-url js-repo-url qa-mirror-repository-url-input', placeholder: _('Input the remote repository URL'), required: true, pattern: "(#{protocols}):\/\/.+", autocomplete: 'new-password'
= text_field_tag :url, nil, class: 'form-control gl-form-input js-mirror-url js-repo-url', placeholder: _('Input the remote repository URL'), required: true, pattern: "(#{protocols}):\/\/.+", autocomplete: 'new-password', data: { qa_selector: 'mirror_repository_url_field' }
= render 'projects/mirrors/instructions'
@ -35,7 +35,7 @@
= link_to _('Learn more.'), help_page_path('user/project/repository/mirror/index.md', anchor: 'mirror-only-protected-branches'), target: '_blank', rel: 'noopener noreferrer'
.panel-footer
= f.submit _('Mirror repository'), class: 'js-mirror-submit qa-mirror-repository-button', name: :update_remote_mirror, pajamas_button: true
= f.submit _('Mirror repository'), class: 'js-mirror-submit', name: :update_remote_mirror, pajamas_button: true, data: { qa_selector: 'mirror_repository_button' }
- else
= render Pajamas::AlertComponent.new(dismissible: false) do |c|
= c.body do

View File

@ -1,7 +1,7 @@
.form-group
= label_tag :mirror_direction, _('Mirror direction'), class: 'label-light'
.select-wrapper
= select_tag :mirror_direction, options_for_select([[_('Push'), 'push']]), class: 'form-control gl-form-select select-control js-mirror-direction qa-mirror-direction', disabled: true
= select_tag :mirror_direction, options_for_select([[_('Push'), 'push']]), class: 'form-control gl-form-select select-control js-mirror-direction', disabled: true, data: { qa_selector: 'mirror_direction_field' }
= sprite_icon('chevron-down', css_class: "gl-icon gl-absolute gl-top-3 gl-right-3 gl-text-gray-200")
= render partial: "projects/mirrors/mirror_repos_push", locals: { f: f }

View File

@ -25,17 +25,17 @@
= render_if_exists 'projects/mirrors/table_pull_row'
- @project.remote_mirrors.each_with_index do |mirror, index|
- next if mirror.new_record?
%tr.rspec-mirrored-repository-row{ class: ('bg-secondary' if mirror.disabled?), data: { qa_selector: 'mirrored_repository_row' } }
%td{ data: { qa_selector: 'mirror_repository_url_cell' } }= mirror.safe_url || _('Invalid URL')
%tr.rspec-mirrored-repository-row{ class: ('bg-secondary' if mirror.disabled?), data: { qa_selector: 'mirrored_repository_row_container' } }
%td{ data: { qa_selector: 'mirror_repository_url_content' } }= mirror.safe_url || _('Invalid URL')
%td= _('Push')
%td
= mirror.last_update_started_at.present? ? time_ago_with_tooltip(mirror.last_update_started_at) : _('Never')
%td{ data: { qa_selector: 'mirror_last_update_at_cell' } }= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never')
%td{ data: { qa_selector: 'mirror_last_update_at_content' } }= mirror.last_update_at.present? ? time_ago_with_tooltip(mirror.last_update_at) : _('Never')
%td
- if mirror.disabled?
= render 'projects/mirrors/disabled_mirror_badge'
- if mirror.last_error.present?
= gl_badge_tag _('Error'), { variant: :danger }, { data: { toggle: 'tooltip', html: 'true', qa_selector: 'mirror_error_badge' }, title: html_escape(mirror.last_error.try(:strip)) }
= gl_badge_tag _('Error'), { variant: :danger }, { data: { toggle: 'tooltip', html: 'true', qa_selector: 'mirror_error_badge_content' }, title: html_escape(mirror.last_error.try(:strip)) }
%td.gl-display-flex
- if mirror_settings_enabled
.btn-group.mirror-actions-group{ role: 'group' }
@ -44,4 +44,4 @@
= render 'shared/remote_mirror_update_button', remote_mirror: mirror
= render Pajamas::ButtonComponent.new(variant: :danger,
icon: 'remove',
button_options: { class: 'js-delete-mirror qa-delete-mirror rspec-delete-mirror', title: _('Remove'), data: { mirror_id: mirror.id, toggle: 'tooltip', container: 'body' } })
button_options: { class: 'js-delete-mirror rspec-delete-mirror', title: _('Remove'), data: { mirror_id: mirror.id, toggle: 'tooltip', container: 'body' } })

View File

@ -19,7 +19,7 @@
= s_("Pipelines|(queued for %{queued_duration})") % { queued_duration: time_interval_in_words(@pipeline.queued_duration)}
- if has_pipeline_badges?(@pipeline)
.well-segment.qa-pipeline-badges
.well-segment
.icon-container
= sprite_icon('flag', css_class: 'gl-top-0!')
- if @pipeline.schedule?

View File

@ -3,14 +3,16 @@
= f.hidden_field(:name)
= dropdown_tag(_('Select branch or create wildcard'),
options: { toggle_class: "js-protected-branch-select js-filter-submit wide monospace qa-protected-branch-select #{toggle_classes}",
options: { toggle_class: "js-protected-branch-select js-filter-submit wide monospace #{toggle_classes}",
filter: true,
dropdown_class: "dropdown-menu-selectable git-revision-dropdown qa-protected-branch-dropdown",
dropdown_class: "dropdown-menu-selectable git-revision-dropdown",
dropdown_qa_selector: "protected_branch_dropdown_content",
placeholder: _("Search protected branches"),
footer_content: true,
data: { show_no: true, show_any: true, show_upcoming: true,
selected: params[:protected_branch_name],
project_id: @project.try(:id) } }) do
project_id: @project.try(:id),
qa_selector: "protected_branch_dropdown" } }) do
%ul.dropdown-footer-list
%li

View File

@ -4,7 +4,7 @@
.settings-header
%h4.settings-title.js-settings-toggle.js-settings-toggle-trigger-only
= s_("ProtectedBranch|Protected branches")
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle qa-expand-protected-branches' }) do
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded ? _('Collapse') : _('Expand')
%p
= s_("ProtectedBranch|Keep stable branches secure and force developers to use merge requests.")

View File

@ -57,7 +57,7 @@
.settings-content
#js-artifacts-settings-app{ data: { full_path: @project.full_path, help_page_path: help_page_path('ci/pipelines/job_artifacts', anchor: 'keep-artifacts-from-most-recent-successful-jobs') } }
%section.qa-variables-settings.settings.no-animate#js-cicd-variables-settings{ class: ('expanded' if expanded), data: { qa_selector: 'variables_settings_content' } }
%section.settings.no-animate#js-cicd-variables-settings{ class: ('expanded' if expanded), data: { qa_selector: 'variables_settings_content' } }
.settings-header
= render 'ci/variables/header', expanded: expanded
.settings-content

View File

@ -3,7 +3,7 @@
- breadcrumb_title @snippet.to_reference
- page_title "#{@snippet.title} (#{@snippet.to_reference})", _("Snippets")
#js-snippet-view{ data: {'qa-selector': 'snippet_view', 'snippet-gid': @snippet.to_global_id, 'report-abuse-path': snippet_report_abuse_path(@snippet), 'can-report-spam': @snippet.submittable_as_spam_by?(current_user).to_s } }
#js-snippet-view{ data: { 'snippet-gid': @snippet.to_global_id, 'report-abuse-path': snippet_report_abuse_path(@snippet), 'can-report-spam': @snippet.submittable_as_spam_by?(current_user).to_s } }
.gl-px-0.gl-py-2
= render 'award_emoji/awards_block', awardable: @snippet, inline: true, api_awards_path: project_snippets_award_api_path(@snippet)

View File

@ -2,8 +2,8 @@
.row.empty-state
.col-12
.svg-content
= image_tag 'illustrations/snippets_empty.svg', data: { qa_selector: 'svg_content' }
.svg-content{ data: { qa_selector: 'svg_content' } }
= image_tag 'illustrations/snippets_empty.svg'
.text-content.gl-text-center.gl-pt-0
- if current_user
%h4

View File

@ -20,7 +20,7 @@
.js-issuable-todo{ data: { project_path: issuable_sidebar[:project_full_path], iid: issuable_sidebar[:iid], id: issuable_sidebar[:id] } }
= form_for issuable_type, url: issuable_sidebar[:issuable_json_path], remote: true, html: { class: 'issuable-context-form inline-update js-issuable-update' } do |f|
.block.assignee.qa-assignee-block{ class: "#{'gl-mt-3' if !signed_in && moved_sidebar_enabled}", data: { qa_selector: 'assignee_block_container' } }
.block.assignee{ class: "#{'gl-mt-3' if !signed_in && moved_sidebar_enabled}", data: { qa_selector: 'assignee_block_container' } }
= render "shared/issuable/sidebar_assignees", issuable_sidebar: issuable_sidebar, assignees: assignees, signed_in: signed_in
- if issuable_sidebar[:supports_severity]

View File

@ -12,7 +12,7 @@
- content_for :prefetch_asset_tags do
- webpack_preload_asset_tag('monaco', prefetch: true)
#js-snippet-view{ data: {'qa-selector': 'snippet_view', 'snippet-gid': @snippet.to_global_id, 'report-abuse-path': snippet_report_abuse_path(@snippet), 'can-report-spam': @snippet.submittable_as_spam_by?(current_user).to_s } }
#js-snippet-view{ data: { 'snippet-gid': @snippet.to_global_id, 'report-abuse-path': snippet_report_abuse_path(@snippet), 'can-report-spam': @snippet.submittable_as_spam_by?(current_user).to_s } }
.row-content-block.top-block.content-component-block.gl-px-0.gl-py-2
= render 'award_emoji/awards_block', awardable: @snippet, inline: true

View File

@ -1,5 +1,8 @@
# frozen_string_literal: true
# DEPRECATED
#
# To be removed by https://gitlab.com/gitlab-org/gitlab/-/issues/366573
class ClusterConfigureIstioWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
@ -10,9 +13,5 @@ class ClusterConfigureIstioWorker # rubocop:disable Scalability/IdempotentWorker
worker_has_external_dependencies!
def perform(cluster_id)
Clusters::Cluster.find_by_id(cluster_id).try do |cluster|
Clusters::Kubernetes::ConfigureIstioIngressService.new(cluster: cluster).execute
end
end
def perform(cluster_id); end
end

View File

@ -21,13 +21,12 @@ class RunPipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
end
def run_pipeline_schedule(schedule, user)
Ci::CreatePipelineService.new(schedule.project,
user,
ref: schedule.ref)
.execute!(:schedule, ignore_skip_ci: true, save_on_errors: false, schedule: schedule)
rescue Ci::CreatePipelineService::CreateError => e
# This is a user operation error such as corrupted .gitlab-ci.yml. Log the error for debugging purpose.
log_extra_metadata_on_done(:pipeline_creation_error, e)
if ::Feature.enabled?(:refactored_create_pipeline_execution_method, schedule.project)
create_pipeline_refactored(schedule, user)
else
create_pipeline_legacy(schedule, user)
end
rescue StandardError => e
error(schedule, e)
end
@ -57,4 +56,24 @@ class RunPipelineScheduleWorker # rubocop:disable Scalability/IdempotentWorker
Gitlab::Metrics.counter(:pipeline_schedule_creation_failed_total,
"Counter of failed attempts of pipeline schedule creation")
end
def create_pipeline_refactored(schedule, user)
response = Ci::CreatePipelineService
.new(schedule.project, user, ref: schedule.ref)
.execute(:schedule, ignore_skip_ci: true, save_on_errors: false, schedule: schedule)
return response if response.payload.persisted?
# This is a user operation error such as corrupted .gitlab-ci.yml. Log the error for debugging purpose.
log_extra_metadata_on_done(:pipeline_creation_error, response.message)
end
def create_pipeline_legacy(schedule, user)
Ci::CreatePipelineService
.new(schedule.project, user, ref: schedule.ref)
.execute!(:schedule, ignore_skip_ci: true, save_on_errors: false, schedule: schedule)
rescue Ci::CreatePipelineService::CreateError => e
# This is a user operation error such as corrupted .gitlab-ci.yml. Log the error for debugging purpose.
log_extra_metadata_on_done(:pipeline_creation_error, e)
end
end

View File

@ -0,0 +1,8 @@
---
name: refactored_create_pipeline_execution_method
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/98965
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/378257
milestone: '15.6'
type: development
group: group::pipeline execution
default_enabled: false

View File

@ -317,7 +317,6 @@ sudo gitlab-rake gitlab:geo:check
to gather the following, basic troubleshooting information.
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
#### Get the number of verification failed repositories
@ -855,7 +854,6 @@ therefore short-circuited. `last_sync_failure` is now set to `The file is missin
examples, but things generally work the same for the other types.
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
#### The Replicator

View File

@ -736,7 +736,6 @@ ApplicationSetting.current
### Open object in `irb`
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
Sometimes it is easier to go through a method if you are in the context of the object. You can shim into the namespace of `Object` to let you open `irb` in the context of any object:

View File

@ -23,7 +23,7 @@ GET /projects/:id/deployments
| `updated_after` | datetime | no | Return deployments updated after the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `updated_before` | datetime | no | Return deployments updated before the specified date. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). |
| `environment` | string | no | The [name of the environment](../ci/environments/index.md) to filter deployments by. |
| `status` | string | no | The status to filter deployments by. One of `created`, `running`, `success`, `failed`, `canceled`, `blocked`.
| `status` | string | no | The status to filter deployments by. One of `created`, `running`, `success`, `failed`, `canceled`, or `blocked`.
```shell
curl --header "PRIVATE-TOKEN: <your_access_token>" "https://gitlab.example.com/api/v4/projects/1/deployments"
@ -487,7 +487,11 @@ curl --request "DELETE" --header "PRIVATE-TOKEN: <your_access_token>" "https://g
Example responses:
```json
{ "message": "202 Accepted" }
{ "message": "204 Deployment destroyed" }
```
```json
{ "message": "403 Forbidden" }
```
```json

View File

@ -70,8 +70,8 @@ POST /projects/:id/feature_flags_user_lists
| Attribute | Type | Required | Description |
| ------------------- | ---------------- | ---------- | ---------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `name` | string | yes | The name of the feature flag. |
| `user_xids` | string | yes | A comma-separated list of user IDs. |
| `name` | string | yes | The name of the list. |
| `user_xids` | string | yes | A comma-separated list of external user IDs. |
```shell
curl "https://gitlab.example.com/api/v4/projects/1/feature_flags_user_lists" \
@ -142,8 +142,8 @@ PUT /projects/:id/feature_flags_user_lists/:iid
| ------------------- | ---------------- | ---------- | ---------------------------------------------------------------------------------------|
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](index.md#namespaced-path-encoding). |
| `iid` | integer/string | yes | The internal ID of the project's feature flag user list. |
| `name` | string | no | The name of the feature flag. |
| `user_xids` | string | no | A comma-separated list of user IDs. |
| `name` | string | no | The name of the list. |
| `user_xids` | string | no | A comma-separated list of external user IDs. |
```shell
curl "https://gitlab.example.com/api/v4/projects/1/feature_flags_user_lists/1" \

View File

@ -151,7 +151,7 @@ POST /projects/:id/feature_flags
| `strategies:name` | JSON | no | The strategy name. Can be `default`, `gradualRolloutUserId`, `userWithId`, or `gitlabUserList`. In [GitLab 13.5](https://gitlab.com/gitlab-org/gitlab/-/issues/36380) and later, can be [`flexibleRollout`](https://docs.getunleash.io/user_guide/activation_strategy#gradual-rollout). |
| `strategies:parameters` | JSON | no | The strategy parameters. |
| `strategies:scopes` | JSON | no | The scopes for the strategy. |
| `strategies:scopes:environment_scope` | string | no | The environment spec for the scope. |
| `strategies:scopes:environment_scope` | string | no | The environment scope of the scope. |
```shell
curl "https://gitlab.example.com/api/v4/projects/1/feature_flags" \
@ -213,8 +213,8 @@ PUT /projects/:id/feature_flags/:feature_flag_name
| `strategies:name` | JSON | no | The strategy name. |
| `strategies:parameters` | JSON | no | The strategy parameters. |
| `strategies:scopes` | JSON | no | The scopes for the strategy. |
| `strategies:scopes:id` | JSON | no | The scopes ID. |
| `strategies:scopes:environment_scope` | string | no | The environment spec for the scope. |
| `strategies:scopes:id` | JSON | no | The environment scope ID. |
| `strategies:scopes:environment_scope` | string | no | The environment scope of the scope. |
```shell
curl "https://gitlab.example.com/api/v4/projects/1/feature_flags/awesome_feature" \

View File

@ -18081,6 +18081,7 @@ Counts of requirements by their state.
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="runnerpermissionsassignrunner"></a>`assignRunner` | [`Boolean!`](#boolean) | Indicates the user can perform `assign_runner` on this resource. |
| <a id="runnerpermissionsdeleterunner"></a>`deleteRunner` | [`Boolean!`](#boolean) | Indicates the user can perform `delete_runner` on this resource. |
| <a id="runnerpermissionsreadrunner"></a>`readRunner` | [`Boolean!`](#boolean) | Indicates the user can perform `read_runner` on this resource. |
| <a id="runnerpermissionsupdaterunner"></a>`updateRunner` | [`Boolean!`](#boolean) | Indicates the user can perform `update_runner` on this resource. |

View File

@ -396,7 +396,19 @@ a project, you can disable this behavior to save space:
You can disable this behavior for all projects on a self-managed instance in the
[instance's CI/CD settings](../../user/admin_area/settings/continuous_integration.md#keep-the-latest-artifacts-for-all-jobs-in-the-latest-successful-pipelines).
## Troubleshooting job artifacts
## Troubleshooting
### Job does not retrieve certain artifacts
By default, jobs fetch all artifacts from previous stages, but jobs using `dependencies`
or `needs` do not fetch artifacts from all jobs by default.
If you use these keywords, artifacts are fetched from only a subset of jobs. Review
the keyword reference for information on how to fetch artifacts with these keywords:
- [`dependencies`](../yaml/index.md#dependencies)
- [`needs`](../yaml/index.md#needs)
- [`needs:artifacts`](../yaml/index.md#needsartifacts)
### Error message `No files to upload`

View File

@ -34,7 +34,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
against the official specification.
- They support [snapshot testing](#markdown-snapshot-testing) of GitLab
internal GLFM processing logic. This is accomplished by automatically
generating YAML ["example snapshot files"](#example-snapshot-files)
generating YAML ["example snapshot files"](#output-example-snapshot-files)
which are used as fixtures to drive automated testing within the GitLab app.
- There are [various scripts and logic](#scripts)
which are used to accomplish the above goals.
@ -104,13 +104,13 @@ serve as input to automated conformance tests. It is
Here are the HTML-rendered versions of the specifications:
- [GitLab Flavored Markdown (GLFM) specification](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output/spec.html), which extends the:
- [GitLab Flavored Markdown (GLFM) specification](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_spec/spec.html), which extends the:
- [GitHub Flavored Markdown (GFM) specification](https://github.github.com/gfm/) (rendered from the [source `spec.txt` for GFM specification](https://github.com/github/cmark-gfm/blob/master/test/spec.txt)), which extends the:
- [CommonMark specification](https://spec.commonmark.org/0.30/) (rendered from the [source `spec.txt` for CommonMark specification](https://github.com/commonmark/commonmark-spec/blob/master/spec.txt))
NOTE:
The creation of the
[HTML-rendered version of the GitLab Flavored Markdown (GLFM) specification](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output/spec.html)
[HTML-rendered version of the GitLab Flavored Markdown (GLFM) specification](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_spec/spec.html)
file is still pending.
However, GLFM has more complex parsing, rendering, and testing requirements than
@ -177,7 +177,7 @@ In this context, it should not be confused with other similar or related meaning
_example_, such as
[RSpec examples](https://relishapp.com/rspec/rspec-core/docs/example-groups/basic-structure-describe-it).
See the section on the [`glfm_official_specification_examples.md`](#glfm_official_specification_examplesmd) file
See the section on the [`glfm_official_specification.md`](#glfm_official_specificationmd) file
for more details on the backtick-delimited Markdown+HTML example syntax.
### Parsers and renderers
@ -341,7 +341,7 @@ The GitLab [Markdown API](../../../api/markdown.md) generates HTML
for a given Markdown string using this method.
The Markdown specified in the [Markdown examples](#markdown-examples) is used to automatically generate HTML in
[`glfm_specification/example_snapshots/html.yml`](#glfm_specificationexample_snapshotshtmlyml) via
[`glfm_specification/output_example_snapshots/html.yml`](#htmlyml) via
[`update-example-snapshots.rb`](#update-example-snapshotsrb-script). These examples are
used when running [Markdown snapshot testing](#markdown-snapshot-testing).
@ -353,7 +353,7 @@ in the ProseMirror WYSIWYG editor.
Just like static HTML,
the Markdown specified in the [Markdown examples](#markdown-examples) is used to automatically generate HTML in
[`glfm_specification/example_snapshots/html.yml`](#glfm_specificationexample_snapshotshtmlyml) via
[`glfm_specification/output_example_snapshots/html.yml`](#htmlyml) via
[`update-example-snapshots.rb`](#update-example-snapshotsrb-script). These examples are
used when running [Markdown snapshot testing](#markdown-snapshot-testing).
@ -387,10 +387,10 @@ Here are more details on the sources of canonical HTML examples:
version of [`spec.txt`](#spectxt).
1. For the examples which are part of the GLFM [_official specification_](#official-specifications),
the canonical HTML is manually maintained and curated via the examples contained in the
[`glfm_official_specification_examples.md`](#glfm_official_specification_examplesmd) [input specification file](#input-specification-files).
[`glfm_official_specification.md`](#glfm_official_specificationmd) [input specification file](#input-specification-files).
1. For the examples which are part of the GLFM [_internal extensions_](#internal-extensions),
the canonical HTML **is never specified**, and **must be left empty in all examples** contained in
the [`glfm_internal_extension_examples.md`](#glfm_internal_extension_examplesmd) [input specification file](#input-specification-files).
the [`glfm_internal_extensions.md`](#glfm_internal_extensionsmd) [input specification file](#input-specification-files).
### Canonicalization of HTML
@ -401,7 +401,7 @@ or HTML elements, to support specific appearance and behavioral requirements.
Neither the backend nor the frontend rendering logic can directly render the clean, basic HTML
which is necessary to perform comparison to the [canonical HTML](#canonical-html)
when running [Markdown conformance testing](#markdown-conformance-testing)
for the [GLFM official specification examples](#glfm_official_specification_examplesmd).
for the [GLFM official specification examples](#glfm_official_specificationmd).
Nor should they be able to, because:
@ -443,7 +443,7 @@ Fixture-based normalization should be used whenever possible, because it is simp
understand than regex-based normalization.
The [Markdown snapshot testing](#markdown-snapshot-testing) uses RSpec to generate the
[example snapshot files](#example-snapshot-files). RSpec enables you to:
[example snapshot files](#output-example-snapshot-files). RSpec enables you to:
- Use the same powerful fixture support and helpers as all the rest of the GitLab RSpec suite.
- Use fixtures to control the state of the database when the example snapshots are generated.
@ -575,10 +575,10 @@ The documentation on the implementation is split into three sections:
1. [Scripts](#scripts).
1. [Specification files](#specification-files).
1. [Example snapshot files](#example-snapshot-files):
1. [Example snapshot files](#output-example-snapshot-files):
These YAML files are used as input data
or fixtures to drive the various tests, and are located under
`glfm_specification/example_snapshots`. All example snapshot files are automatically
`glfm_specification/output_example_snapshots`. All example snapshot files are automatically
generated based on the specification files and the implementation of the parsers and renderers.
However, they can also be directly edited if necessary, such as to
test-drive an incomplete implementation.
@ -620,8 +620,8 @@ end
subgraph input:<br/>input specification files
C[ghfm_spec_v_0.29.md] --> A
D[glfm_intro.md] --> A
E[glfm_official_specification_examples.md] --> A
F[glfm_internal_extension_examples.md] --> A
E[glfm_official_specification.md] --> A
F[glfm_internal_extensions.md] --> A
end
subgraph output:<br/>GLFM specification files
A --> G[spec.txt]
@ -646,7 +646,7 @@ script, which expects canonical HTML, against the GitLab renderer implementation
`scripts/glfm/run-spec-tests.sh` is a convenience shell script which runs
conformance specs via the CommonMark standard `spec_tests.py` script,
which uses the `glfm_specification/output/spec.txt` file and `scripts/glfm/canonicalize-html.rb`
which uses the `glfm_specification/output_spec/spec.txt` file and `scripts/glfm/canonicalize-html.rb`
helper script to test the GLFM renderer implementations' support for rendering Markdown
specification examples to canonical HTML.
@ -672,9 +672,9 @@ end
#### `update-example-snapshots.rb` script
The `scripts/glfm/update-example-snapshots.rb` script uses the GLFM
`glfm_specification/output/spec.txt` specification file and the
`glfm_specification/output_spec/spec.txt` specification file and the
`glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml`
file to create and update the [example snapshot](#example-snapshot-files)
file to create and update the [example snapshot](#output-example-snapshot-files)
YAML files:
```mermaid
@ -746,7 +746,7 @@ runs the [`update-specification.rb`](#update-specificationrb-script).
It fails with an exception and non-zero return code if running these scripts
results in any diffs to the generated and committed
[output specification files](#output-specification-files) or
[example snapshot files](#example-snapshot-files).
[example snapshot files](#output-example-snapshot-files).
This script is run via the `glfm-verify` CI job to ensure that all changes to the
[input specification files](#input-specification-files)
@ -766,12 +766,16 @@ subcategories based on their usage and purpose:
[`ghfm_spec_v_0.29.md`](#github-flavored-markdown-specification) specification.
- `gitlab_flavored_markdown`: Contains all `glfm_*` files.
- `*.md` [input specification files](#input-specification-files),
which represent the GLFM specification itself.
which represent the source of truth for the GLFM specification and all associated examples.
- `*.yml` [input specification configuration files](#input-specification-configuration-files),
which control various aspects of the automated GLFM scripts and processes.
- `output`: Contains [output specification files](#output-specification-files),
which are automatically generated from the
- `output_spec`: Contains the `spec.txt` and `spec.html` [output specification files](#output-specification-files),
which represent the [GLFM official specification](#official-specifications), and are automatically generated from the
input files by running the [`update-specification.rb`](#update-specificationrb-script) script.
- `output_example_snapshots`: Contains [output example snapshot files](#output-example-snapshot-files).
which are used to drive [snapshot testing](#markdown-snapshot-testing), and are automatically generated from the
input files by running the [`update-specification.rb`](#update-specificationrb-script)
and [`scripts/glfm/update-example-snapshots.rb`](#update-example-snapshotsrb-script) scripts.
#### Input specification files
@ -790,31 +794,22 @@ is a copy of the official latest [GFM `spec.txt`](https://github.com/github/cmar
- When it is downloaded, the version number is added to the filename.
- The extension is changed from `*.txt` to `*.md` so that it can be handled better by Markdown editors.
It currently contains additional **Introduction** and **Appendix** prose-only header sections which do not
contain any examples.
All header sections which contain examples are expected to be contained within a contiguous file section
which is delimited by:
1. The beginning of the second H1 header (the first one after the **Introduction** section)
1. An `<!-- END TESTS -->` HTML comment line.
NOTE:
For extra clarity, this file uses the `ghfm` acronym in its name instead of `gfm`, as
explained in the [Acronyms section](#acronyms-glfm-ghfm-gfm-commonmark).
##### `glfm_intro.md`
##### `glfm_official_specification.md`
[`glfm_specification/input/gitlab_flavored_markdown/glfm_intro.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/input/gitlab_flavored_markdown/glfm_intro.md)
is the GitLab-specific version of the prose in the introduction section of the GLFM specification.
- It is manually updated.
- The `update-specification.rb` script inserts it into the generated GLFM `spec.txt` to replace
the GitHub-specific GFM version of the introductory section.
##### `glfm_canonical_examples.txt`
The `glfm_canonical_examples.txt` file is deprecated and no longer exists. It has been replaced by two files:
- [`glfm_official_specification_examples.md`](#glfm_official_specification_examplesmd)
which contains the [GLFM official specification](#official-specifications) examples.
- [`glfm_internal_extension_examples.md`](#glfm_internal_extension_examplesmd)
which contains the [GLFM internal extension](#internal-extensions) examples.
##### `glfm_official_specification_examples.md`
[`glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification_examples.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification_examples.md)
[`glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification.md)
consists of the manually updated Markdown+HTML examples for the
[GLFM official specification](#official-specifications), and their associated documentation and descriptions.
@ -832,7 +827,12 @@ consists of the manually updated Markdown+HTML examples for the
- `H3` header sections must be nested within `H2` header sections. They cannot be
nested directly within `H1` header sections.
`glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification_examples.md` sample entries:
It _may_ contain additional prose-only header sections which do not contain any examples.
All header sections which contain examples _must_ be contained within a contiguous file section which
is delimited by `<!-- BEGIN TESTS -->` and `<!-- END TESTS -->` HTML comment lines.
`glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification.md` sample entries:
<!-- markdownlint-disable MD048 -->
@ -864,13 +864,13 @@ bold
<!-- markdownlint-enable MD048 -->
##### `glfm_internal_extension_examples.md`
##### `glfm_internal_extensions.md`
[`glfm_specification/input/gitlab_flavored_markdown/glfm_internal_extension_examples.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/input/gitlab_flavored_markdown/glfm_internal_extension_examples.md)
[`glfm_specification/input/gitlab_flavored_markdown/glfm_internal_extensions.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/input/gitlab_flavored_markdown/glfm_internal_extensions.md)
consists of the manually updated Markdown examples for the
[GLFM internal extensions](#internal-extensions), and their associated documentation and descriptions.
Its general format is identical to [`glfm_official_specification_examples.md`](#glfm_official_specification_examplesmd),
Its general format is identical to [`glfm_official_specification.md`](#glfm_official_specificationmd),
consisting of `H1`, `H2`, or `H3` sections containing [Markdown examples](#markdown-examples) in the
[standard backtick-delimited `spec.txt` format](#various-markdown-specifications).
@ -878,7 +878,12 @@ However, as described in the [canonical HTML section](#canonical-html), only the
example is specified, and the HTML portion is left empty, because internal extension examples are
never used for [Markdown conformance testing](#markdown-conformance-testing).
`glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification_examples.md` sample entries:
It _may_ contain additional prose-only header sections which do not contain any examples.
All header sections which contain examples _must_ be contained within a contiguous file section which
is delimited by `<!-- BEGIN TESTS -->` and `<!-- END TESTS -->` HTML comment lines.
`glfm_specification/input/gitlab_flavored_markdown/glfm_official_specification.md` sample entries:
NOTE:
All lines in this example are prefixed with a `|` character. This prefix helps avoid false
@ -908,7 +913,7 @@ See the main [specification files](#specification-files) section for more contex
All of the manually curated example names in the configuration files must correspond to
an existing [Markdown example](#markdown-examples) name found in
[`example_snapshots/examples_index.yml`](#glfm_specificationexample_snapshotsexamples_indexyml),
[`output_example_snapshots/examples_index.yml`](#examples_indexyml),
which is automatically generated based on the [input specification files](#input-specification-files).
If there is an invalid reference to an example name that does not exist, the
@ -943,16 +948,16 @@ controls the behavior of the [scripts](#scripts) and [tests](#types-of-markdown-
The following optional entries are supported for each example. They all default to `false`:
- `skip_update_example_snapshots`: When true, skips any addition or update of any this example's entries
in the [`glfm_specification/example_snapshots/html.yml`](#glfm_specificationexample_snapshotshtmlyml) file
or the [`glfm_specification/example_snapshots/prosemirror_json.yml`](#glfm_specificationexample_snapshotsprosemirror_jsonyml) file.
in the [`glfm_specification/output_example_snapshots/html.yml`](#htmlyml) file
or the [`glfm_specification/output_example_snapshots/prosemirror_json.yml`](#prosemirror_jsonyml) file.
If this value is truthy, then no other `skip_update_example_snapshot_*` entries can be truthy,
and an error is raised if any of them are.
- `skip_update_example_snapshot_html_static`: When true, skips addition or update of this example's [static HTML](#static-html)
entry in the [`glfm_specification/example_snapshots/html.yml`](#glfm_specificationexample_snapshotshtmlyml) file.
entry in the [`glfm_specification/output_example_snapshots/html.yml`](#htmlyml) file.
- `skip_update_example_snapshot_html_wysiwyg`: When true, skips addition or update of this example's [WYSIWYG HTML](#wysiwyg-html)
entry in the [`glfm_specification/example_snapshots/html.yml`](#glfm_specificationexample_snapshotshtmlyml) file.
entry in the [`glfm_specification/output_example_snapshots/html.yml`](#htmlyml) file.
- `skip_update_example_snapshot_prosemirror_json`: When true, skips addition or update of this example's
entry in the [`glfm_specification/example_snapshots/prosemirror_json.yml`](#glfm_specificationexample_snapshotsprosemirror_jsonyml) file.
entry in the [`glfm_specification/output_example_snapshots/prosemirror_json.yml`](#prosemirror_jsonyml) file.
- `skip_running_conformance_static_tests`: When true, skips running the [Markdown conformance tests](#markdown-conformance-testing)
of the [static HTML](#static-html) for this example.
- `skip_running_conformance_wysiwyg_tests`: When true, skips running the [Markdown conformance tests](#markdown-conformance-testing)
@ -962,7 +967,7 @@ The following optional entries are supported for each example. They all default
- `skip_running_snapshot_wysiwyg_html_tests`: When true, skips running the [Markdown snapshot tests](#markdown-snapshot-testing)
of the [WYSIWYG HTML](#wysiwyg-html) for this example.
- `skip_running_snapshot_prosemirror_json_tests`: When true, skips running the [Markdown snapshot tests](#markdown-snapshot-testing)
of the [ProseMirror JSON](#glfm_specificationexample_snapshotsprosemirror_jsonyml) for this example.
of the [ProseMirror JSON](#prosemirror_jsonyml) for this example.
`glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml` sample entry:
@ -1033,9 +1038,9 @@ or [environment-variable-based normalization](#environment-variable-based-normal
snapshot:
07_01_00_href: *07_01_00_href
07_01_00_id: *07_01_00_id
wysiwyg:
07_01_00_href: *07_01_00_href
07_01_00_id: *07_01_00_id
wysiwyg:
07_01_00_href: *07_01_00_href
07_01_00_id: *07_01_00_id
prosemirror_json:
07_01_00_href: *07_01_00_href
07_01_00_id: *07_01_00_id
@ -1093,36 +1098,66 @@ move or copy a hosted version of the rendered HTML `spec.html` version to anothe
##### spec.txt
[`glfm_specification/output/spec.txt`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output/spec.txt)
[`glfm_specification/output_spec/spec.txt`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_spec/spec.txt)
is a Markdown specification file, in the standard format
with prose and Markdown + canonical HTML examples.
It also serves as input for other scripts such as `update-example-snapshots.rb`
and `run-spec-tests.sh`.
It also serves as input for other scripts such as
`run-spec-tests.sh`.
It is generated or updated by the `update-specification.rb` script, using the
[input specification files](#input-specification-files) as input.
See the [`update-specification.rb` script section](#update-specificationrb-script)
for a diagram and more description on this process.
NOTE:
Even though `spec.txt` is a Markdown file, it is named with a `*.txt` extension
for consistency with the GFM and CommonMark specifications. All other GLFM
Markdown files are named with a `*.md` extension for compatibility with
various editors to enable Markdown formatting and syntax highlighting.
##### spec.html
[`glfm_specification/output/spec.html`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output/spec.html)
[`glfm_specification/output_spec/spec.html`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_spec/spec.html)
is an HTML file, rendered based on `spec.txt`. It is
also generated (or updated) by the `update-specification.rb` script at the same time as
generated (or updated) by the `update-specification.rb` script at the same time as
`spec.txt`.
It corresponds to the HTML-rendered versions of the
"GitHub Flavored Markdown" (<abbr title="GitHub Flavored Markdown">GFM</abbr>)
[specification](https://github.github.com/gfm/)
and the [CommonMark specification](https://spec.commonmark.org/0.30/).
and the [CommonMark specification](https://spec.commonmark.org/0.30/), but only
contains GitLab Flavored Markdown (GLFM) examples.
### Example snapshot files
NOTE:
The `example_snapshots` directory contains files which are generated by the
`update-example-snapshots.rb` script based off of the files in the
`glfm_specification/input` directory. They are used as fixtures to drive the
various Markdown snapshot tests.
The formatting of this HTML is currently not identical to the GFM and CommonMark
HTML-rendered specification. It is only the raw output of running `spec.txt` through
the GitLab Markdown renderer. Properly formatting the HTML will require
duplicating or reusing the Lua script and template from the CommonMark project:
[CommonMark Makefile](https://github.com/commonmark/commonmark-spec/blob/master/Makefile#L11)
#### Output example snapshot files
The `output_example_snapshots` directory contains files which are generated by the
`update-specification.rb` and `update-example-snapshots.rb` scripts based off of the files in the
`glfm_specification/input` directory.
The `output-specification.rb` script generates
`output_snapshot_examples/glfm_snapshot_spec.md` and `output_snapshot_examples/glfm_snapshot_spec.html`.
These files are Markdown specification files containing examples generated based on input files,
similar to the `output_spec/spec.txt` and `output_spec/spec.html`, with the following differences:
1. They contain a superset of _all_ examples from
the Commonmark, GitHub Flavored Markdown, and GitLab Flavored Markdown specifications, whereas
`spec.*` only contains the GLFM specification. This is to provide a single place to refer to
all examples when working with [snapshot testing](#markdown-snapshot-testing).
1. They contain _only_ header sections which contain examples. They do not contain any prose-only
sections which do not contain examples.
The `update-example-snapshots.rb` script generates the various
`output_snapshot_examples/*.yml` files, which
are used as fixtures to drive the [snapshot testing](#markdown-snapshot-testing).
After the entire GLFM implementation is complete for both backend (Ruby) and
frontend (JavaScript), all of these YAML files can be automatically generated.
@ -1131,9 +1166,47 @@ key in `glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.ym
can be used to disable automatic generation of some examples. They can instead
be manually edited as necessary to help drive the implementations.
#### `glfm_specification/example_snapshots/examples_index.yml`
##### `glfm_snapshot_spec.md`
[`glfm_specification/example_snapshots/examples_index.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/example_snapshots/examples_index.yml)
[`glfm_specification/output_example_snapshots/glfm_snapshot_spec.md`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_example_snapshots/glfm_snapshot_spec.md)
is a Markdown file, containing standard Markdown + canonical HTML examples like [`spec.txt`](#spectxt).
It is generated or updated by the `update-specification.rb` script, using the
[input specification files](#input-specification-files) as input.
See the [`update-specification.rb` script section](#update-specificationrb-script)
for a diagram and more description on this process. It also serves as input for other
scripts such as `update-example-snapshots.rb`.
It is similar to [`spec.txt`](#spectxt), with the following differences:
1. [`spec.txt`](#spectxt) contains only examples for GitLab Flavored Markdown, but
`glfm_snapshot_spec.md` also contains the full superset of examples from the
"GitHub Flavored Markdown" (<abbr title="GitHub Flavored Markdown">GFM</abbr>)[specification](https://github.github.com/gfm/)
and the [CommonMark specification](https://spec.commonmark.org/0.30/) specifications.
1. [`spec.txt`](#spectxt) represents the full GLFM specification, including additional header sections
containing only explanatory prose and no examples, but `glfm_snapshot_spec.md` consists of only
header sections which contain examples. This is because its purpose is to serve as input for
the other [`output example snapshot files`](#output-example-snapshot-files) - it is not intended
to serve as an actual [specification file](#output-specification-files)
like [`spec.txt`](#spectxt) or [`spec.html`](#spechtml).
##### `glfm_snapshot_spec.html`
[`glfm_specification/output_snapshot_examples/glfm_snapshot_spec.html`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_snapshot_examples/glfm_snapshot_spec.html)
is an HTML file, rendered based on `glfm_snapshot_spec.md`. It is
generated (or updated) by the `update-specification.rb` script at the same time as
`glfm_snapshot_spec.md`.
NOTE:
The formatting of this HTML is currently not identical to the GFM and CommonMark
HTML-rendered specification. It is only the raw output of running `glfm_snapshot_spec.md` through
the GitLab Markdown renderer. Properly formatting the HTML will require
duplicating or reusing the Lua script and template from the CommonMark project:
[CommonMark Makefile](https://github.com/commonmark/commonmark-spec/blob/master/Makefile#L11)
##### `examples_index.yml`
[`glfm_specification/output_example_snapshots/examples_index.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_example_snapshots/examples_index.yml)
is the main list of all
CommonMark, GFM, and GLFM example names, each with a uniquely identifying name.
@ -1142,9 +1215,9 @@ CommonMark, GFM, and GLFM example names, each with a uniquely identifying name.
- For CommonMark and GFM examples,
these sections originally came from the GFM `spec.txt`.
- For GLFM examples, it is generated from
[`glfm_official_specification_examples.md`](#glfm_official_specification_examplesmd) and [`glfm_internal_extension_examples.md`](#glfm_internal_extension_examplesmd).
[`glfm_official_specification.md`](#glfm_official_specificationmd) and [`glfm_internal_extensions.md`](#glfm_internal_extensionsmd).
- It also contains extra metadata about each example, such as:
1. `spec_txt_example_position` - The position of the example in the generated GLFM `spec.txt` file.
1. `spec_example_position` - The position of the example in the generated GLFM `spec.txt` file.
- This value is the index order of each individual Markdown + HTML5 example in the file. It is _not_
the line number in the file.
- This value can be used to locate the example in the rendered `spec.html` file, because the standard
@ -1159,49 +1232,49 @@ CommonMark, GFM, and GLFM example names, each with a uniquely identifying name.
examples where multiple examples exist for the same Section 7 subsection are
added to the end of the sub-section.
`glfm_specification/example_snapshots/examples_index.yml` sample entries:
`examples_index.yml` sample entries:
```yaml
02_01_00_preliminaries_characters_and_lines_1:
spec_txt_example_position: 1
spec_example_position: 1
source_specification: commonmark
03_01_00_blocks_and_inlines_precedence_1:
spec_txt_example_position: 12
spec_example_position: 12
source_specification: commonmark
05_03_00_container_blocks_task_list_items_1:
spec_txt_example_position: 279
spec_example_position: 279
source_specification: github
06_04_00_inlines_emphasis_and_strong_emphasis_1:
spec_txt_example_position: 360
spec_example_position: 360
source_specification: github
07_01_00_audio_link_1:
spec_txt_example_position: 301
spec_example_position: 301
source_specification: gitlab
```
#### `glfm_specification/example_snapshots/markdown.yml`
##### `markdown.yml`
[`glfm_specification/example_snapshots/markdown.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/example_snapshots/markdown.yml) contains the original Markdown
for each entry in `glfm_specification/example_snapshots/examples_index.yml`
[`glfm_specification/output_example_snapshots/markdown.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_example_snapshots/markdown.yml) contains the original Markdown
for each entry in `glfm_specification/output_example_snapshots/examples_index.yml`:
- For CommonMark and GFM Markdown,
it is generated (or updated) from the standard GFM
`spec.txt` using the `update-example-snapshots.rb` script.
- For GLFM, it is generated (or updated) from the
[`glfm_official_specification_examples.md`](#glfm_official_specification_examplesmd) and [`glfm_internal_extension_examples.md`](#glfm_internal_extension_examplesmd)
[`glfm_official_specification.md`](#glfm_official_specificationmd) and [`glfm_internal_extensions.md`](#glfm_internal_extensionsmd)
input specification files.
`glfm_specification/example_snapshots/markdown.yml` sample entry:
`glfm_specification/output_example_snapshots/markdown.yml` sample entry:
```yaml
06_04_00_inlines_emphasis_and_strong_emphasis_1: |
*foo bar*
```
#### `glfm_specification/example_snapshots/html.yml`
##### `html.yml`
[`glfm_specification/example_snapshots/html.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/example_snapshots/html.yml)
contains the HTML for each entry in `glfm_specification/example_snapshots/examples_index.yml`
[`glfm_specification/output_example_snapshots/html.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_example_snapshots/html.yml)
contains the HTML for each entry in `glfm_specification/output_example_snapshots/examples_index.yml`:
Three types of entries exist, with different HTML for each:
@ -1209,16 +1282,16 @@ Three types of entries exist, with different HTML for each:
- The ["Canonical"](#canonicalization-of-html) HTML.
- For CommonMark and GFM examples, the HTML comes from the examples in `spec.txt`.
- For [GLFM official specification](#official-specifications) examples, it is generated/updated from
[`glfm_official_specification_examples.md`](#glfm_official_specification_examplesmd).
[`glfm_official_specification.md`](#glfm_official_specificationmd).
- **Static**
- This is the static (backend (Ruby)-generated) HTML for each entry in
`glfm_specification/example_snapshots/examples_index.yml`.
`glfm_specification/output_example_snapshots/examples_index.yml`.
- It is generated/updated from backend [Markdown API](../../../api/markdown.md)
(or the underlying internal classes) via the `update-example-snapshots.rb` script,
but can be manually updated for static examples with incomplete implementations.
- **WYSIWYG**
- The WYSIWYG (frontend, JavaScript-generated) HTML for each entry in
`glfm_specification/example_snapshots/examples_index.yml`.
`glfm_specification/output_example_snapshots/examples_index.yml`.
- It is generated (or updated) from the frontend Content Editor implementation via the
`update-example-snapshots.rb` script. It can be manually updated for WYSIWYG
examples with incomplete implementations.
@ -1226,7 +1299,7 @@ Three types of entries exist, with different HTML for each:
Any exceptions or failures which occur when generating HTML are replaced with an
`Error - check implementation` value.
`glfm_specification/example_snapshots/html.yml` sample entry:
`glfm_specification/output_example_snapshots/html.yml` sample entry:
```yaml
06_04_00_inlines_emphasis_and_strong_emphasis_1:
@ -1242,16 +1315,16 @@ NOTE:
The actual `static` or `WYSIWYG` entries may differ from the example `html.yml`,
depending on how the implementations evolve.
#### `glfm_specification/example_snapshots/prosemirror_json.yml`
##### `prosemirror_json.yml`
[`glfm_specification/example_snapshots/prosemirror_json.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/example_snapshots/prosemirror_json.yml)
contains the ProseMirror JSON for each entry in `glfm_specification/example_snapshots/examples_index.yml`
[`glfm_specification/output_example_snapshots/prosemirror_json.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/glfm_specification/output_example_snapshots/prosemirror_json.yml)
contains the ProseMirror JSON for each entry in `glfm_specification/output_example_snapshots/examples_index.yml`
- It is generated (or updated) from the frontend code via the `update-example-snapshots.rb`
script, but can be manually updated for examples with incomplete implementations.
- Any exceptions or failures when generating are replaced with a `Error - check implementation` value.
`glfm_specification/example_snapshots/prosemirror_json.yml` sample entry:
`glfm_specification/output_example_snapshots/prosemirror_json.yml` sample entry:
```yaml
06_04_00_inlines_emphasis_and_strong_emphasis_1: |-
@ -1291,13 +1364,13 @@ This section describes how the scripts can be used to manage the GLFM specificat
1. If you are working on an in-progress feature or bug, make any necessary manual updates to the [input specification files](#input-specification-files). This may include:
1. Updating the canonical Markdown or HTML examples in
[`glfm_official_specification_examples.md`](#glfm_official_specification_examplesmd) or [`glfm_internal_extension_examples.md`](#glfm_internal_extension_examplesmd).
[`glfm_official_specification.md`](#glfm_official_specificationmd) or [`glfm_internal_extensions.md`](#glfm_internal_extensionsmd).
1. Updating `glfm_specification/input/gitlab_flavored_markdown/glfm_example_status.yml` to reflect the current status of the examples or tests.
1. Run [`update-specification.rb`](#update-specificationrb-script) to update the `spec.txt` to reflect any changes which were made to the [input specification files](#input-specification-files).
1. Visually inspect and confirm any resulting changes to the [output specification files](#output-specification-files).
1. Run [`update-example-snapshots.rb`](#update-example-snapshotsrb-script) to update the [example snapshot files](#example-snapshot-files).
1. Visually inspect and confirm any resulting changes to the [example snapshot files](#example-snapshot-files).
1. Run [`update-example-snapshots.rb`](#update-example-snapshotsrb-script) to update the [example snapshot files](#output-example-snapshot-files).
1. Visually inspect and confirm any resulting changes to the [example snapshot files](#output-example-snapshot-files).
1. Run [`run-snapshot-tests.sh`](#run-snapshot-testssh-script) as a convenience script to run all relevant frontend (RSpec) and backend (Jest) tests which use the example snapshots.
1. Any frontend or backend snapshot test may also be run individually.
1. All frontend and backend tests are also run as part of the continuous integration suite, as they normally are.
1. Commit any changes to the [input specification files](#input-specification-files), [output specification files](#output-specification-files), or [example snapshot files](#example-snapshot-files).
1. Commit any changes to the [input specification files](#input-specification-files), [output specification files](#output-specification-files), or [example snapshot files](#output-example-snapshot-files).

View File

@ -44,8 +44,9 @@ You can quickly see which comments involve you, because
mentions for yourself (the user currently signed in) are highlighted
in a different color.
Avoid mentioning `@all` in issues and merge requests, because it sends an email notification
to all the members of that project's group. This might be interpreted as spam.
Avoid mentioning `@all` in issues and merge requests. It sends an email notification
to all members of that project's parent group, not only the participants of the project,
and may be interpreted as spam.
Notifications and mentions can be disabled in
[a group's settings](../group/manage.md#disable-email-notifications).

View File

@ -823,7 +823,6 @@ Group.find_by_sql("SELECT * FROM namespaces WHERE name LIKE '%oup'")
If transferring a group doesn't work through the UI or API, you may want to attempt the transfer in a [Rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session):
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
```ruby
@ -855,7 +854,6 @@ At times, a group deletion may get stuck. If needed, in a [Rails console session
you can attempt to delete a group using the following command:
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
```ruby

View File

@ -121,3 +121,13 @@ To resolve this, ensure that:
- If you have set the `TF_HTTP_PASSWORD` CI/CD variable, make sure that you either:
- Set the same value as `TF_PASSWORD`
- Remove `TF_HTTP_PASSWORD` variable if your CI/CD job does not explicitly use it.
### Enable Developer role access to destructive commands
To permit a user with the Developer role to run destructive commands, you need a workaround:
1. [Create a project access token](../../project/settings/project_access_tokens.md#create-a-project-access-token) with `api` scope.
1. Add `TF_USERNAME` and `TF_PASSWORD` to your CI/CD variables:
1. Set the value of `TF_USERNAME` to the username of your project access token.
1. Set the value of `TF_PASSWORD` to the password of your project access token.
1. Optional. Protect the variables to make them only available in pipelines that run on protected branches or protected tags.

View File

@ -134,7 +134,6 @@ To disable notifications for all projects that have Slack integration enabled,
[start a rails console session](../../../administration/operations/rails_console.md#starting-a-rails-console-session) and use a script similar to the following:
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
```ruby

View File

@ -326,7 +326,6 @@ repository mirroring, mirroring breaks when people leave the company. Use this
script to migrate disparate mirroring users and tokens into a single service account:
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
```ruby

View File

@ -304,7 +304,6 @@ For example, to enable **Check whether the commit author is a GitLab user** and
and create a filter for allowing commits from a specific email domain only through rails console:
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
``` ruby

View File

@ -516,7 +516,6 @@ If a project or repository has been updated but the state is not reflected in th
You can do so through [a Rails console session](../../administration/operations/rails_console.md#starting-a-rails-console-session) and one of the following:
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
```ruby
@ -546,7 +545,6 @@ end
If a project cannot be deleted, you can attempt to delete it through [Rails console](../../administration/operations/rails_console.md#starting-a-rails-console-session).
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
```ruby
@ -571,7 +569,6 @@ To toggle a specific feature, you can [start a Rails console session](../../admi
and run the following function:
WARNING:
Commands that change data can cause damage if not run correctly or under the right conditions. Always run commands in a test environment first and have a backup instance ready to restore.
```ruby

View File

@ -1,3 +1,4 @@
<!-- BEGIN TESTS -->
# GitLab Internal Extension Markdown
## Audio
@ -109,3 +110,4 @@ also requires an EE license enabling the `group_wikis` feature:
.
<p><a href="group-wikis-test-file">group-wikis-test-file</a></p>
````````````````````````````````
<!-- END TESTS -->

View File

@ -1,3 +0,0 @@
# Introduction
TODO: Write a GitLab-specific version of the GitHub Flavored Markdown intro section.

View File

@ -1,7 +1,12 @@
# Introduction
TODO: Write a GitLab-specific version of the GitHub Flavored Markdown intro section.
<!-- BEGIN TESTS -->
# GitLab Official Specification Markdown
Currently, only some of the GitLab-specific markdown features are
listed in this section. We will eventually add all
listed in this section. We may eventually add all
GitLab-specific features currently listed as supported in the
[user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html).
@ -275,3 +280,4 @@ A table of contents can be indented with up to three spaces.
</nav>
<h1>Heading 1</h1>
````````````````````````````````
<!-- END TESTS -->

View File

@ -2,11 +2,6 @@
title: GitLab Flavored Markdown (GLFM) Spec
version: alpha
...
# Introduction
TODO: Write a GitLab-specific version of the GitHub Flavored Markdown intro section.
# Preliminaries
## Characters and lines
@ -9603,7 +9598,7 @@ Multiple spaces
# GitLab Official Specification Markdown
Currently, only some of the GitLab-specific markdown features are
listed in this section. We will eventually add all
listed in this section. We may eventually add all
GitLab-specific features currently listed as supported in the
[user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html).
@ -9989,342 +9984,3 @@ also requires an EE license enabling the `group_wikis` feature:
.
<p><a href="group-wikis-test-file">group-wikis-test-file</a></p>
````````````````````````````````
<!-- END TESTS -->
# Appendix: A parsing strategy
In this appendix we describe some features of the parsing strategy
used in the CommonMark reference implementations.
## Overview
Parsing has two phases:
1. In the first phase, lines of input are consumed and the block
structure of the document---its division into paragraphs, block quotes,
list items, and so on---is constructed. Text is assigned to these
blocks but not parsed. Link reference definitions are parsed and a
map of links is constructed.
2. In the second phase, the raw text contents of paragraphs and headings
are parsed into sequences of Markdown inline elements (strings,
code spans, links, emphasis, and so on), using the map of link
references constructed in phase 1.
At each point in processing, the document is represented as a tree of
**blocks**. The root of the tree is a `document` block. The `document`
may have any number of other blocks as **children**. These children
may, in turn, have other blocks as children. The last child of a block
is normally considered **open**, meaning that subsequent lines of input
can alter its contents. (Blocks that are not open are **closed**.)
Here, for example, is a possible document tree, with the open blocks
marked by arrows:
``` tree
-> document
-> block_quote
paragraph
"Lorem ipsum dolor\nsit amet."
-> list (type=bullet tight=true bullet_char=-)
list_item
paragraph
"Qui *quodsi iracundia*"
-> list_item
-> paragraph
"aliquando id"
```
## Phase 1: block structure
Each line that is processed has an effect on this tree. The line is
analyzed and, depending on its contents, the document may be altered
in one or more of the following ways:
1. One or more open blocks may be closed.
2. One or more new blocks may be created as children of the
last open block.
3. Text may be added to the last (deepest) open block remaining
on the tree.
Once a line has been incorporated into the tree in this way,
it can be discarded, so input can be read in a stream.
For each line, we follow this procedure:
1. First we iterate through the open blocks, starting with the
root document, and descending through last children down to the last
open block. Each block imposes a condition that the line must satisfy
if the block is to remain open. For example, a block quote requires a
`>` character. A paragraph requires a non-blank line.
In this phase we may match all or just some of the open
blocks. But we cannot close unmatched blocks yet, because we may have a
[lazy continuation line].
2. Next, after consuming the continuation markers for existing
blocks, we look for new block starts (e.g. `>` for a block quote).
If we encounter a new block start, we close any blocks unmatched
in step 1 before creating the new block as a child of the last
matched block.
3. Finally, we look at the remainder of the line (after block
markers like `>`, list markers, and indentation have been consumed).
This is text that can be incorporated into the last open
block (a paragraph, code block, heading, or raw HTML).
Setext headings are formed when we see a line of a paragraph
that is a [setext heading underline].
Reference link definitions are detected when a paragraph is closed;
the accumulated text lines are parsed to see if they begin with
one or more reference link definitions. Any remainder becomes a
normal paragraph.
We can see how this works by considering how the tree above is
generated by four lines of Markdown:
``` markdown
> Lorem ipsum dolor
sit amet.
> - Qui *quodsi iracundia*
> - aliquando id
```
At the outset, our document model is just
``` tree
-> document
```
The first line of our text,
``` markdown
> Lorem ipsum dolor
```
causes a `block_quote` block to be created as a child of our
open `document` block, and a `paragraph` block as a child of
the `block_quote`. Then the text is added to the last open
block, the `paragraph`:
``` tree
-> document
-> block_quote
-> paragraph
"Lorem ipsum dolor"
```
The next line,
``` markdown
sit amet.
```
is a "lazy continuation" of the open `paragraph`, so it gets added
to the paragraph's text:
``` tree
-> document
-> block_quote
-> paragraph
"Lorem ipsum dolor\nsit amet."
```
The third line,
``` markdown
> - Qui *quodsi iracundia*
```
causes the `paragraph` block to be closed, and a new `list` block
opened as a child of the `block_quote`. A `list_item` is also
added as a child of the `list`, and a `paragraph` as a child of
the `list_item`. The text is then added to the new `paragraph`:
``` tree
-> document
-> block_quote
paragraph
"Lorem ipsum dolor\nsit amet."
-> list (type=bullet tight=true bullet_char=-)
-> list_item
-> paragraph
"Qui *quodsi iracundia*"
```
The fourth line,
``` markdown
> - aliquando id
```
causes the `list_item` (and its child the `paragraph`) to be closed,
and a new `list_item` opened up as child of the `list`. A `paragraph`
is added as a child of the new `list_item`, to contain the text.
We thus obtain the final tree:
``` tree
-> document
-> block_quote
paragraph
"Lorem ipsum dolor\nsit amet."
-> list (type=bullet tight=true bullet_char=-)
list_item
paragraph
"Qui *quodsi iracundia*"
-> list_item
-> paragraph
"aliquando id"
```
## Phase 2: inline structure
Once all of the input has been parsed, all open blocks are closed.
We then "walk the tree," visiting every node, and parse raw
string contents of paragraphs and headings as inlines. At this
point we have seen all the link reference definitions, so we can
resolve reference links as we go.
``` tree
document
block_quote
paragraph
str "Lorem ipsum dolor"
softbreak
str "sit amet."
list (type=bullet tight=true bullet_char=-)
list_item
paragraph
str "Qui "
emph
str "quodsi iracundia"
list_item
paragraph
str "aliquando id"
```
Notice how the [line ending] in the first paragraph has
been parsed as a `softbreak`, and the asterisks in the first list item
have become an `emph`.
### An algorithm for parsing nested emphasis and links
By far the trickiest part of inline parsing is handling emphasis,
strong emphasis, links, and images. This is done using the following
algorithm.
When we're parsing inlines and we hit either
- a run of `*` or `_` characters, or
- a `[` or `![`
we insert a text node with these symbols as its literal content, and we
add a pointer to this text node to the [delimiter stack](@).
The [delimiter stack] is a doubly linked list. Each
element contains a pointer to a text node, plus information about
- the type of delimiter (`[`, `![`, `*`, `_`)
- the number of delimiters,
- whether the delimiter is "active" (all are active to start), and
- whether the delimiter is a potential opener, a potential closer,
or both (which depends on what sort of characters precede
and follow the delimiters).
When we hit a `]` character, we call the *look for link or image*
procedure (see below).
When we hit the end of the input, we call the *process emphasis*
procedure (see below), with `stack_bottom` = NULL.
#### *look for link or image*
Starting at the top of the delimiter stack, we look backwards
through the stack for an opening `[` or `![` delimiter.
- If we don't find one, we return a literal text node `]`.
- If we do find one, but it's not *active*, we remove the inactive
delimiter from the stack, and return a literal text node `]`.
- If we find one and it's active, then we parse ahead to see if
we have an inline link/image, reference link/image, compact reference
link/image, or shortcut reference link/image.
+ If we don't, then we remove the opening delimiter from the
delimiter stack and return a literal text node `]`.
+ If we do, then
* We return a link or image node whose children are the inlines
after the text node pointed to by the opening delimiter.
* We run *process emphasis* on these inlines, with the `[` opener
as `stack_bottom`.
* We remove the opening delimiter.
* If we have a link (and not an image), we also set all
`[` delimiters before the opening delimiter to *inactive*. (This
will prevent us from getting links within links.)
#### *process emphasis*
Parameter `stack_bottom` sets a lower bound to how far we
descend in the [delimiter stack]. If it is NULL, we can
go all the way to the bottom. Otherwise, we stop before
visiting `stack_bottom`.
Let `current_position` point to the element on the [delimiter stack]
just above `stack_bottom` (or the first element if `stack_bottom`
is NULL).
We keep track of the `openers_bottom` for each delimiter
type (`*`, `_`) and each length of the closing delimiter run
(modulo 3). Initialize this to `stack_bottom`.
Then we repeat the following until we run out of potential
closers:
- Move `current_position` forward in the delimiter stack (if needed)
until we find the first potential closer with delimiter `*` or `_`.
(This will be the potential closer closest
to the beginning of the input -- the first one in parse order.)
- Now, look back in the stack (staying above `stack_bottom` and
the `openers_bottom` for this delimiter type) for the
first matching potential opener ("matching" means same delimiter).
- If one is found:
+ Figure out whether we have emphasis or strong emphasis:
if both closer and opener spans have length >= 2, we have
strong, otherwise regular.
+ Insert an emph or strong emph node accordingly, after
the text node corresponding to the opener.
+ Remove any delimiters between the opener and closer from
the delimiter stack.
+ Remove 1 (for regular emph) or 2 (for strong emph) delimiters
from the opening and closing text nodes. If they become empty
as a result, remove them and remove the corresponding element
of the delimiter stack. If the closing node is removed, reset
`current_position` to the next element in the stack.
- If none is found:
+ Set `openers_bottom` to the element before `current_position`.
(We know that there are no openers for this kind of closer up to and
including this point, so this puts a lower bound on future searches.)
+ If the closer at `current_position` is not a potential opener,
remove it from the delimiter stack (since we know it can't
be a closer either).
+ Advance `current_position` to the next element in the stack.
After we're done, we remove all delimiters above `stack_bottom` from the
delimiter stack.

View File

@ -0,0 +1,276 @@
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="1:1-4:3" class="code highlight js-syntax-highlight language-yaml" lang="yaml" data-lang-params="frontmatter" v-pre="true"><code><span id="LC1" class="line" lang="yaml"><span class="na">title</span><span class="pi">:</span> <span class="s">GitLab Flavored Markdown (GLFM) Spec</span></span>
<span id="LC2" class="line" lang="yaml"><span class="na">version</span><span class="pi">:</span> <span class="s">alpha</span></span></code></pre>
<copy-code></copy-code>
</div>
<h1 data-sourcepos="5:1-5:14" dir="auto">
<a id="user-content-introduction" class="anchor" href="#introduction" aria-hidden="true"></a>Introduction</h1>
<p data-sourcepos="7:1-7:84" dir="auto">TODO: Write a GitLab-specific version of the GitHub Flavored Markdown intro section.</p>
<h1 data-sourcepos="10:1-10:40" dir="auto">
<a id="user-content-gitlab-official-specification-markdown" class="anchor" href="#gitlab-official-specification-markdown" aria-hidden="true"></a>GitLab Official Specification Markdown</h1>
<p data-sourcepos="12:1-15:104" dir="auto">Currently, only some of the GitLab-specific markdown features are
listed in this section. We may eventually add all
GitLab-specific features currently listed as supported in the
<a href="https://docs.gitlab.com/ee/user/markdown.html" rel="nofollow noreferrer noopener" target="_blank">user-facing documentation for GitLab Flavored Markdown</a>.</p>
<p data-sourcepos="17:1-18:69" dir="auto">There is currently only this single top-level heading, but the
examples may be split into multiple top-level headings in the future.</p>
<h2 data-sourcepos="20:1-20:12" dir="auto">
<a id="user-content-footnotes" class="anchor" href="#footnotes" aria-hidden="true"></a>Footnotes</h2>
<p data-sourcepos="22:1-23:143" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#footnotes" rel="nofollow noreferrer noopener" target="_blank">the footnotes section of the user-facing documentation for GitLab Flavored Markdown</a>.</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="25:1-49:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">footnote reference tag [^fortytwo]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">[^fortytwo]: footnote text</span>
<span id="LC4" class="line" lang="plaintext">.</span>
<span id="LC5" class="line" lang="plaintext">&lt;p&gt;</span>
<span id="LC6" class="line" lang="plaintext">footnote reference tag</span>
<span id="LC7" class="line" lang="plaintext">&lt;sup&gt;</span>
<span id="LC8" class="line" lang="plaintext">&lt;a href="#fn-fortytwo-42" id="fnref-fortytwo-42" data-footnote-ref&gt;</span>
<span id="LC9" class="line" lang="plaintext">1</span>
<span id="LC10" class="line" lang="plaintext">&lt;/a&gt;</span>
<span id="LC11" class="line" lang="plaintext">&lt;/sup&gt;</span>
<span id="LC12" class="line" lang="plaintext">&lt;/p&gt;</span>
<span id="LC13" class="line" lang="plaintext">&lt;section data-footnotes&gt;</span>
<span id="LC14" class="line" lang="plaintext">&lt;ol&gt;</span>
<span id="LC15" class="line" lang="plaintext">&lt;li id="fn-fortytwo-42"&gt;</span>
<span id="LC16" class="line" lang="plaintext">&lt;p&gt;</span>
<span id="LC17" class="line" lang="plaintext">footnote text</span>
<span id="LC18" class="line" lang="plaintext">&lt;a href="#fnref-fortytwo-42" data-footnote-backref&gt;</span>
<span id="LC19" class="line" lang="plaintext">&lt;/a&gt;</span>
<span id="LC20" class="line" lang="plaintext">&lt;/p&gt;</span>
<span id="LC21" class="line" lang="plaintext">&lt;/li&gt;</span>
<span id="LC22" class="line" lang="plaintext">&lt;/ol&gt;</span>
<span id="LC23" class="line" lang="plaintext">&lt;/section&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<h2 data-sourcepos="51:1-51:18" dir="auto">
<a id="user-content-task-list-items" class="anchor" href="#task-list-items" aria-hidden="true"></a>Task list items</h2>
<p data-sourcepos="53:1-54:117" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#task-lists" rel="nofollow noreferrer noopener" target="_blank">Task lists</a> in the GitLab Flavored Markdown documentation.</p>
<p data-sourcepos="56:1-59:39" dir="auto">Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above.
GitLab extends the behavior of task list items to support additional features.
Some of these features are in-progress, and should not yet be considered part of the official
GitLab Flavored Markdown specification.</p>
<p data-sourcepos="61:1-61:85" dir="auto">Some of the behavior of task list items is implemented as client-side JavaScript/CSS.</p>
<p data-sourcepos="63:1-63:80" dir="auto">The following are some basic examples; more examples may be added in the future.</p>
<p data-sourcepos="65:1-65:16" dir="auto">Incomplete task:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="67:1-77:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [ ] incomplete</span>
<span id="LC2" class="line" lang="plaintext">.</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;task-button/&gt;</span>
<span id="LC6" class="line" lang="plaintext">&lt;input type="checkbox" disabled/&gt;</span>
<span id="LC7" class="line" lang="plaintext">incomplete</span>
<span id="LC8" class="line" lang="plaintext">&lt;/li&gt;</span>
<span id="LC9" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<p data-sourcepos="79:1-79:15" dir="auto">Completed task:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="81:1-91:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [x] completed</span>
<span id="LC2" class="line" lang="plaintext">.</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;task-button/&gt;</span>
<span id="LC6" class="line" lang="plaintext">&lt;input type="checkbox" checked disabled/&gt;</span>
<span id="LC7" class="line" lang="plaintext">completed</span>
<span id="LC8" class="line" lang="plaintext">&lt;/li&gt;</span>
<span id="LC9" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<p data-sourcepos="93:1-93:18" dir="auto">Inapplicable task:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="95:1-107:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [~] inapplicable</span>
<span id="LC2" class="line" lang="plaintext">.</span>
<span id="LC3" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC4" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC5" class="line" lang="plaintext">&lt;task-button/&gt;</span>
<span id="LC6" class="line" lang="plaintext">&lt;input type="checkbox" data-inapplicable disabled&gt;</span>
<span id="LC7" class="line" lang="plaintext">&lt;s&gt;</span>
<span id="LC8" class="line" lang="plaintext">inapplicable</span>
<span id="LC9" class="line" lang="plaintext">&lt;/s&gt;</span>
<span id="LC10" class="line" lang="plaintext">&lt;/li&gt;</span>
<span id="LC11" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<p data-sourcepos="109:1-110:50" dir="auto">Inapplicable task in a "loose" list. Note that the <code>&lt;del&gt;</code> tag is not applied to the
loose text; it has strikethrough applied with CSS.</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="112:1-131:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">- [~] inapplicable</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"> text in loose list</span>
<span id="LC4" class="line" lang="plaintext">.</span>
<span id="LC5" class="line" lang="plaintext">&lt;ul&gt;</span>
<span id="LC6" class="line" lang="plaintext">&lt;li&gt;</span>
<span id="LC7" class="line" lang="plaintext">&lt;p&gt;</span>
<span id="LC8" class="line" lang="plaintext">&lt;task-button/&gt;</span>
<span id="LC9" class="line" lang="plaintext">&lt;input type="checkbox" data-inapplicable disabled&gt;</span>
<span id="LC10" class="line" lang="plaintext">&lt;s&gt;</span>
<span id="LC11" class="line" lang="plaintext">inapplicable</span>
<span id="LC12" class="line" lang="plaintext">&lt;/s&gt;</span>
<span id="LC13" class="line" lang="plaintext">&lt;/p&gt;</span>
<span id="LC14" class="line" lang="plaintext">&lt;p&gt;</span>
<span id="LC15" class="line" lang="plaintext">text in loose list</span>
<span id="LC16" class="line" lang="plaintext">&lt;/p&gt;</span>
<span id="LC17" class="line" lang="plaintext">&lt;/li&gt;</span>
<span id="LC18" class="line" lang="plaintext">&lt;/ul&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<h2 data-sourcepos="133:1-133:15" dir="auto">
<a id="user-content-front-matter" class="anchor" href="#front-matter" aria-hidden="true"></a>Front matter</h2>
<p data-sourcepos="135:1-136:121" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#front-matter" rel="nofollow noreferrer noopener" target="_blank">Front matter</a> in the GitLab Flavored Markdown documentation.</p>
<p data-sourcepos="138:1-139:95" dir="auto">Front matter is metadata included at the beginning of a Markdown document, preceding the content.
This data can be used by static site generators like Jekyll, Hugo, and many other applications.</p>
<p data-sourcepos="141:1-141:18" dir="auto">YAML front matter:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="143:1-153:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">---</span>
<span id="LC2" class="line" lang="plaintext">title: YAML front matter</span>
<span id="LC3" class="line" lang="plaintext">---</span>
<span id="LC4" class="line" lang="plaintext">.</span>
<span id="LC5" class="line" lang="plaintext">&lt;pre&gt;</span>
<span id="LC6" class="line" lang="plaintext">&lt;code&gt;</span>
<span id="LC7" class="line" lang="plaintext">title: YAML front matter</span>
<span id="LC8" class="line" lang="plaintext">&lt;/code&gt;</span>
<span id="LC9" class="line" lang="plaintext">&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<p data-sourcepos="155:1-155:18" dir="auto">TOML front matter:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="157:1-167:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">+++</span>
<span id="LC2" class="line" lang="plaintext">title: TOML front matter</span>
<span id="LC3" class="line" lang="plaintext">+++</span>
<span id="LC4" class="line" lang="plaintext">.</span>
<span id="LC5" class="line" lang="plaintext">&lt;pre&gt;</span>
<span id="LC6" class="line" lang="plaintext">&lt;code&gt;</span>
<span id="LC7" class="line" lang="plaintext">title: TOML front matter</span>
<span id="LC8" class="line" lang="plaintext">&lt;/code&gt;</span>
<span id="LC9" class="line" lang="plaintext">&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<p data-sourcepos="169:1-169:18" dir="auto">JSON front matter:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="171:1-185:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">;;;</span>
<span id="LC2" class="line" lang="plaintext">{</span>
<span id="LC3" class="line" lang="plaintext"> "title": "JSON front matter"</span>
<span id="LC4" class="line" lang="plaintext">}</span>
<span id="LC5" class="line" lang="plaintext">;;;</span>
<span id="LC6" class="line" lang="plaintext">.</span>
<span id="LC7" class="line" lang="plaintext">&lt;pre&gt;</span>
<span id="LC8" class="line" lang="plaintext">&lt;code&gt;</span>
<span id="LC9" class="line" lang="plaintext">{</span>
<span id="LC10" class="line" lang="plaintext"> "title": "JSON front matter"</span>
<span id="LC11" class="line" lang="plaintext">}</span>
<span id="LC12" class="line" lang="plaintext">&lt;/code&gt;</span>
<span id="LC13" class="line" lang="plaintext">&lt;/pre&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<p data-sourcepos="187:1-187:66" dir="auto">Front matter blocks should be inserted at the top of the document:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="189:1-199:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">text</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext">---</span>
<span id="LC4" class="line" lang="plaintext">title: YAML front matter</span>
<span id="LC5" class="line" lang="plaintext">---</span>
<span id="LC6" class="line" lang="plaintext">.</span>
<span id="LC7" class="line" lang="plaintext">&lt;p&gt;text&lt;/p&gt;</span>
<span id="LC8" class="line" lang="plaintext">&lt;hr&gt;</span>
<span id="LC9" class="line" lang="plaintext">&lt;h2&gt;title: YAML front matter&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<p data-sourcepos="201:1-201:74" dir="auto">Front matter block delimiters shouldnt be preceded by space characters:</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="203:1-210:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> ---</span>
<span id="LC2" class="line" lang="plaintext">title: YAML front matter</span>
<span id="LC3" class="line" lang="plaintext">---</span>
<span id="LC4" class="line" lang="plaintext">.</span>
<span id="LC5" class="line" lang="plaintext">&lt;hr&gt;</span>
<span id="LC6" class="line" lang="plaintext">&lt;h2&gt;title: YAML front matter&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<h2 data-sourcepos="212:1-212:20" dir="auto">
<a id="user-content-table-of-contents" class="anchor" href="#table-of-contents" aria-hidden="true"></a>Table of contents</h2>
<p data-sourcepos="214:1-216:46" dir="auto">See
<a href="https://docs.gitlab.com/ee/user/markdown.html#table-of-contents" rel="nofollow noreferrer noopener" target="_blank">table of contents</a>
in the GitLab Flavored Markdown documentation.</p>
<p data-sourcepos="218:1-219:58" dir="auto">A table of contents is an unordered list that links to subheadings in the document.
Add either the <code>[[_TOC_]]</code> or </p><ul class="section-nav">
<li><a href="#introduction">Introduction</a></li>
<li>
<a href="#gitlab-official-specification-markdown">GitLab Official Specification Markdown</a><ul>
<li><a href="#footnotes">Footnotes</a></li>
<li><a href="#task-list-items">Task list items</a></li>
<li><a href="#front-matter">Front matter</a></li>
<li><a href="#table-of-contents">Table of contents</a></li>
</ul>
</li>
</ul> tag on its own line.
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="221:1-238:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[TOC]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"># Heading 1</span>
<span id="LC4" class="line" lang="plaintext"></span>
<span id="LC5" class="line" lang="plaintext">## Heading 2</span>
<span id="LC6" class="line" lang="plaintext">.</span>
<span id="LC7" class="line" lang="plaintext">&lt;nav&gt;</span>
<span id="LC8" class="line" lang="plaintext"> &lt;ul&gt;</span>
<span id="LC9" class="line" lang="plaintext"> &lt;li&gt;&lt;a href="#heading-1"&gt;Heading 1&lt;/a&gt;&lt;/li&gt;</span>
<span id="LC10" class="line" lang="plaintext"> &lt;ul&gt;</span>
<span id="LC11" class="line" lang="plaintext"> &lt;li&gt;&lt;a href="#heading-2"&gt;Heading 2&lt;/a&gt;&lt;/li&gt;</span>
<span id="LC12" class="line" lang="plaintext"> &lt;/ul&gt;</span>
<span id="LC13" class="line" lang="plaintext"> &lt;/ul&gt;</span>
<span id="LC14" class="line" lang="plaintext">&lt;/nav&gt;</span>
<span id="LC15" class="line" lang="plaintext">&lt;h1&gt;Heading 1&lt;/h1&gt;</span>
<span id="LC16" class="line" lang="plaintext">&lt;h2&gt;Heading 2&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="240:1-257:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[_TOC_]]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"># Heading 1</span>
<span id="LC4" class="line" lang="plaintext"></span>
<span id="LC5" class="line" lang="plaintext">## Heading 2</span>
<span id="LC6" class="line" lang="plaintext">.</span>
<span id="LC7" class="line" lang="plaintext">&lt;nav&gt;</span>
<span id="LC8" class="line" lang="plaintext"> &lt;ul&gt;</span>
<span id="LC9" class="line" lang="plaintext"> &lt;li&gt;&lt;a href="#heading-1"&gt;Heading 1&lt;/a&gt;&lt;/li&gt;</span>
<span id="LC10" class="line" lang="plaintext"> &lt;ul&gt;</span>
<span id="LC11" class="line" lang="plaintext"> &lt;li&gt;&lt;a href="#heading-2"&gt;Heading 2&lt;/a&gt;&lt;/li&gt;</span>
<span id="LC12" class="line" lang="plaintext"> &lt;/ul&gt;</span>
<span id="LC13" class="line" lang="plaintext"> &lt;/ul&gt;</span>
<span id="LC14" class="line" lang="plaintext">&lt;/nav&gt;</span>
<span id="LC15" class="line" lang="plaintext">&lt;h1&gt;Heading 1&lt;/h1&gt;</span>
<span id="LC16" class="line" lang="plaintext">&lt;h2&gt;Heading 2&lt;/h2&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<p data-sourcepos="259:1-260:5" dir="auto">A table of contents is a block element. It should preceded and followed by a blank
line.</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="262:1-271:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext">[[_TOC_]]</span>
<span id="LC2" class="line" lang="plaintext">text</span>
<span id="LC3" class="line" lang="plaintext"></span>
<span id="LC4" class="line" lang="plaintext">text</span>
<span id="LC5" class="line" lang="plaintext">[TOC]</span>
<span id="LC6" class="line" lang="plaintext">.</span>
<span id="LC7" class="line" lang="plaintext">&lt;p&gt;[[&lt;em&gt;TOC&lt;/em&gt;]]text&lt;/p&gt;</span>
<span id="LC8" class="line" lang="plaintext">&lt;p&gt;text[TOC]&lt;/p&gt;</span></code></pre>
<copy-code></copy-code>
</div>
<p data-sourcepos="273:1-273:60" dir="auto">A table of contents can be indented with up to three spaces.</p>
<div class="gl-relative markdown-code-block js-markdown-code">
<pre data-sourcepos="275:1-286:32" class="code highlight js-syntax-highlight language-plaintext" lang="plaintext" data-canonical-lang="example" v-pre="true"><code><span id="LC1" class="line" lang="plaintext"> [[_TOC_]]</span>
<span id="LC2" class="line" lang="plaintext"></span>
<span id="LC3" class="line" lang="plaintext"># Heading 1</span>
<span id="LC4" class="line" lang="plaintext">.</span>
<span id="LC5" class="line" lang="plaintext">&lt;nav&gt;</span>
<span id="LC6" class="line" lang="plaintext"> &lt;ul&gt;</span>
<span id="LC7" class="line" lang="plaintext"> &lt;li&gt;&lt;a href="#heading-1"&gt;Heading 1&lt;/a&gt;&lt;/li&gt;</span>
<span id="LC8" class="line" lang="plaintext"> &lt;/ul&gt;</span>
<span id="LC9" class="line" lang="plaintext">&lt;/nav&gt;</span>
<span id="LC10" class="line" lang="plaintext">&lt;h1&gt;Heading 1&lt;/h1&gt;</span></code></pre>
<copy-code></copy-code>
</div>

View File

@ -0,0 +1,287 @@
---
title: GitLab Flavored Markdown (GLFM) Spec
version: alpha
...
# Introduction
TODO: Write a GitLab-specific version of the GitHub Flavored Markdown intro section.
<!-- BEGIN TESTS -->
# GitLab Official Specification Markdown
Currently, only some of the GitLab-specific markdown features are
listed in this section. We may eventually add all
GitLab-specific features currently listed as supported in the
[user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html).
There is currently only this single top-level heading, but the
examples may be split into multiple top-level headings in the future.
## Footnotes
See
[the footnotes section of the user-facing documentation for GitLab Flavored Markdown](https://docs.gitlab.com/ee/user/markdown.html#footnotes).
```````````````````````````````` example gitlab
footnote reference tag [^fortytwo]
[^fortytwo]: footnote text
.
<p>
footnote reference tag
<sup>
<a href="#fn-fortytwo-42" id="fnref-fortytwo-42" data-footnote-ref>
1
</a>
</sup>
</p>
<section data-footnotes>
<ol>
<li id="fn-fortytwo-42">
<p>
footnote text
<a href="#fnref-fortytwo-42" data-footnote-backref>
</a>
</p>
</li>
</ol>
</section>
````````````````````````````````
## Task list items
See
[Task lists](https://docs.gitlab.com/ee/user/markdown.html#task-lists) in the GitLab Flavored Markdown documentation.
Task list items (checkboxes) are defined as a GitHub Flavored Markdown extension in a section above.
GitLab extends the behavior of task list items to support additional features.
Some of these features are in-progress, and should not yet be considered part of the official
GitLab Flavored Markdown specification.
Some of the behavior of task list items is implemented as client-side JavaScript/CSS.
The following are some basic examples; more examples may be added in the future.
Incomplete task:
```````````````````````````````` example gitlab
- [ ] incomplete
.
<ul>
<li>
<task-button/>
<input type="checkbox" disabled/>
incomplete
</li>
</ul>
````````````````````````````````
Completed task:
```````````````````````````````` example gitlab
- [x] completed
.
<ul>
<li>
<task-button/>
<input type="checkbox" checked disabled/>
completed
</li>
</ul>
````````````````````````````````
Inapplicable task:
```````````````````````````````` example gitlab
- [~] inapplicable
.
<ul>
<li>
<task-button/>
<input type="checkbox" data-inapplicable disabled>
<s>
inapplicable
</s>
</li>
</ul>
````````````````````````````````
Inapplicable task in a "loose" list. Note that the `<del>` tag is not applied to the
loose text; it has strikethrough applied with CSS.
```````````````````````````````` example gitlab
- [~] inapplicable
text in loose list
.
<ul>
<li>
<p>
<task-button/>
<input type="checkbox" data-inapplicable disabled>
<s>
inapplicable
</s>
</p>
<p>
text in loose list
</p>
</li>
</ul>
````````````````````````````````
## Front matter
See
[Front matter](https://docs.gitlab.com/ee/user/markdown.html#front-matter) in the GitLab Flavored Markdown documentation.
Front matter is metadata included at the beginning of a Markdown document, preceding the content.
This data can be used by static site generators like Jekyll, Hugo, and many other applications.
YAML front matter:
```````````````````````````````` example gitlab
---
title: YAML front matter
---
.
<pre>
<code>
title: YAML front matter
</code>
</pre>
````````````````````````````````
TOML front matter:
```````````````````````````````` example gitlab
+++
title: TOML front matter
+++
.
<pre>
<code>
title: TOML front matter
</code>
</pre>
````````````````````````````````
JSON front matter:
```````````````````````````````` example gitlab
;;;
{
"title": "JSON front matter"
}
;;;
.
<pre>
<code>
{
"title": "JSON front matter"
}
</code>
</pre>
````````````````````````````````
Front matter blocks should be inserted at the top of the document:
```````````````````````````````` example gitlab
text
---
title: YAML front matter
---
.
<p>text</p>
<hr>
<h2>title: YAML front matter</h2>
````````````````````````````````
Front matter block delimiters shouldnt be preceded by space characters:
```````````````````````````````` example gitlab
---
title: YAML front matter
---
.
<hr>
<h2>title: YAML front matter</h2>
````````````````````````````````
## Table of contents
See
[table of contents](https://docs.gitlab.com/ee/user/markdown.html#table-of-contents)
in the GitLab Flavored Markdown documentation.
A table of contents is an unordered list that links to subheadings in the document.
Add either the `[[_TOC_]]` or `[TOC]` tag on its own line.
```````````````````````````````` example gitlab
[TOC]
# Heading 1
## Heading 2
.
<nav>
<ul>
<li><a href="#heading-1">Heading 1</a></li>
<ul>
<li><a href="#heading-2">Heading 2</a></li>
</ul>
</ul>
</nav>
<h1>Heading 1</h1>
<h2>Heading 2</h2>
````````````````````````````````
```````````````````````````````` example gitlab
[[_TOC_]]
# Heading 1
## Heading 2
.
<nav>
<ul>
<li><a href="#heading-1">Heading 1</a></li>
<ul>
<li><a href="#heading-2">Heading 2</a></li>
</ul>
</ul>
</nav>
<h1>Heading 1</h1>
<h2>Heading 2</h2>
````````````````````````````````
A table of contents is a block element. It should preceded and followed by a blank
line.
```````````````````````````````` example gitlab
[[_TOC_]]
text
text
[TOC]
.
<p>[[<em>TOC</em>]]text</p>
<p>text[TOC]</p>
````````````````````````````````
A table of contents can be indented with up to three spaces.
```````````````````````````````` example gitlab
[[_TOC_]]
# Heading 1
.
<nav>
<ul>
<li><a href="#heading-1">Heading 1</a></li>
</ul>
</nav>
<h1>Heading 1</h1>
````````````````````````````````
<!-- END TESTS -->

View File

@ -9,8 +9,8 @@ module QA
def self.included(base)
super
base.view 'app/assets/javascripts/lazy_loader.js' do
element :js_lazy_loaded
base.view 'app/views/layouts/_img_loader.html.haml' do
element :js_lazy_loaded_content
end
end
end

View File

@ -38,7 +38,7 @@ module QA
# webdriver to miss the hit so we wait for the svg to load before
# clicking the button.
within_element(:svg_content) do
has_element?(:js_lazy_loaded)
has_element?(:js_lazy_loaded_content)
end
click_element(:create_first_page_link)

View File

@ -12,10 +12,6 @@ module QA
element :editor_toolbar_button
end
view 'app/views/projects/blob/_editor.html.haml' do
element :source_editor_preview_container
end
def has_markdown_preview?(component, content)
within_element(:source_editor_preview_container) do
has_css?(component, exact_text: content)

View File

@ -11,7 +11,7 @@ module QA
include Shared::Editor
view 'app/views/projects/blob/_editor.html.haml' do
element :file_name, "text_field_tag 'file_name'" # rubocop:disable QA/ElementWithPattern
element :file_name_field
end
view 'app/views/projects/blob/_template_selectors.html.haml' do
@ -23,7 +23,7 @@ module QA
end
def add_name(name)
fill_in 'file_name', with: name
fill_element(:file_name_field, with: name)
end
def select_template(template_type, template)

View File

@ -11,7 +11,7 @@ module QA
super
base.view 'app/views/projects/blob/_editor.html.haml' do
element :editor
element :source_editor_preview_container
end
end
@ -30,7 +30,7 @@ module QA
private
def text_area
within_element :editor do
within_element :source_editor_preview_container do
find('textarea', visible: false)
end
end

View File

@ -23,7 +23,7 @@ module QA
# This can cause webdriver to miss the hit so we wait for the svg to load (implicitly with has_element?)
# before clicking the button.
within_element(:label_svg_content) do
has_element?(:js_lazy_loaded)
has_element?(:js_lazy_loaded_content)
end
click_element :create_new_label_button

View File

@ -16,22 +16,22 @@ module QA
end
view 'app/views/projects/branches/_branch.html.haml' do
element :branch_name
element :badge_content
element :branch_container
element :branch_link
end
view 'app/views/projects/branches/_panel.html.haml' do
element :all_branches
element :all_branches_container
end
view 'app/views/projects/branches/index.html.haml' do
element :delete_merged_branches
element :delete_merged_branches_link
end
def delete_branch(branch_name)
within_element(:all_branches) do
within(".js-branch-#{branch_name}") do
click_element(:delete_branch_button)
end
within_element(:branch_container, name: branch_name) do
click_element(:delete_branch_button)
end
click_element(:delete_branch_confirmation_button)
@ -41,22 +41,20 @@ module QA
def has_no_branch?(branch_name, reload: false)
wait_until(reload: reload) do
within_element(:all_branches) do
has_no_element?(:branch_name, text: branch_name)
within_element(:all_branches_container) do
has_no_element?(:branch_link, text: branch_name)
end
end
end
def has_branch_with_badge?(branch_name, badge)
within_element(:all_branches) do
within(".js-branch-#{branch_name} .badge") do
has_text?(badge)
end
within_element(:branch_container, name: branch_name) do
has_element?(:badge_content, text: badge)
end
end
def delete_merged_branches
click_element(:delete_merged_branches)
click_element(:delete_merged_branches_link)
click_confirmation_ok_button
end
end

View File

@ -35,10 +35,6 @@ module QA
element :status_icon, 'ci-status-icon-${status}' # rubocop:disable QA/ElementWithPattern
end
view 'app/views/projects/pipelines/_info.html.haml' do
element :pipeline_badges
end
view 'app/assets/javascripts/pipelines/components/graph/job_group_dropdown.vue' do
element :job_dropdown_container
element :jobs_dropdown_menu
@ -68,12 +64,6 @@ module QA
has_no_element?(:job_link, text: job_name)
end
def has_tag?(tag_name)
within_element(:pipeline_badges) do
has_selector?('.badge', text: tag_name)
end
end
def linked_pipelines
all_elements(:linked_pipeline_container, minimum: 1)
end

View File

@ -6,25 +6,25 @@ module QA
module Settings
class MirroringRepositories < Page::Base
view 'app/views/projects/mirrors/_authentication_method.html.haml' do
element :authentication_method
element :password
element :authentication_method_field
element :password_field
end
view 'app/views/projects/mirrors/_mirror_repos.html.haml' do
element :mirror_repository_url_input
element :mirror_repository_url_field
element :mirror_repository_button
end
view 'app/views/projects/mirrors/_mirror_repos_list.html.haml' do
element :mirror_repository_url_cell
element :mirror_last_update_at_cell
element :mirror_error_badge
element :mirrored_repository_row
element :mirror_repository_url_content
element :mirror_last_update_at_content
element :mirror_error_badge_content
element :mirrored_repository_row_container
element :copy_public_key_button
end
view 'app/views/projects/mirrors/_mirror_repos_form.html.haml' do
element :mirror_direction
element :mirror_direction_field
end
view 'app/views/shared/_remote_mirror_update_button.html.haml' do
@ -37,28 +37,23 @@ module QA
element :fingerprints_list
end
view 'app/views/projects/mirrors/_authentication_method.html.haml' do
element :authentication_method
element :password
end
def repository_url=(value)
fill_element :mirror_repository_url_input, value
fill_element :mirror_repository_url_field, value
end
def password=(value)
fill_element :password, value
fill_element :password_field, value
end
def mirror_direction=(value)
raise ArgumentError, "Mirror direction must be 'Push' or 'Pull'" unless %w[Push Pull].include?(value)
select_element(:mirror_direction, value)
select_element(:mirror_direction_field, value)
# Changing the mirror direction causes the fields below to change,
# and that change is animated, so we need to wait for the animation
# to complete otherwise changes to those fields could fail
wait_for_animated_element :authentication_method
wait_for_animated_element :authentication_method_field
end
def authentication_method=(value)
@ -66,13 +61,13 @@ module QA
raise ArgumentError, "Authentication method must be 'SSH public key', 'Password', or 'None'"
end
select_element(:authentication_method, value)
select_element(:authentication_method_field, value)
end
def public_key(url)
row_index = find_repository_row_index url
within_element_by_index(:mirrored_repository_row, row_index) do
within_element_by_index(:mirrored_repository_row_container, row_index) do
find_element(:copy_public_key_button)['data-clipboard-text']
end
end
@ -92,7 +87,7 @@ module QA
def update(url)
row_index = find_repository_row_index(url)
within_element_by_index(:mirrored_repository_row, row_index) do
within_element_by_index(:mirrored_repository_row_container, row_index) do
# When a repository is first mirrored, the update process might
# already be started, so the button is already "clicked"
click_element :update_now_button unless has_element? :updating_button
@ -105,16 +100,16 @@ module QA
row_index = find_repository_row_index(url)
wait_until(sleep_interval: 1) do
within_element_by_index(:mirrored_repository_row, row_index) do
last_update = find_element(:mirror_last_update_at_cell, wait: 0)
within_element_by_index(:mirrored_repository_row_container, row_index) do
last_update = find_element(:mirror_last_update_at_content, wait: 0)
last_update.has_text?('just now') || last_update.has_text?('seconds')
end
end
# Fail early if the page still shows that there has been no update
within_element_by_index(:mirrored_repository_row, row_index) do
find_element(:mirror_last_update_at_cell, wait: 0).assert_no_text('Never')
assert_no_element(:mirror_error_badge)
within_element_by_index(:mirrored_repository_row_container, row_index) do
find_element(:mirror_last_update_at_content, wait: 0).assert_no_text('Never')
assert_no_element(:mirror_error_badge_content)
end
end
@ -122,7 +117,7 @@ module QA
def find_repository_row_index(target_url)
wait_until(max_duration: 5, reload: false) do
all_elements(:mirror_repository_url_cell, minimum: 1).index do |url|
all_elements(:mirror_repository_url_content, minimum: 1).index do |url|
# The url might be a sanitized url but the target_url won't be so
# we compare just the paths instead of the full url
URI.parse(url.text).path == target_url.path

View File

@ -6,8 +6,8 @@ module QA
module Settings
class ProtectedBranches < Page::Base
view 'app/views/projects/protected_branches/shared/_dropdown.html.haml' do
element :protected_branch_select
element :protected_branch_dropdown
element :protected_branch_dropdown_content
end
view 'app/views/projects/protected_branches/_create_protected_branch.html.haml' do
@ -22,9 +22,9 @@ module QA
end
def select_branch(branch_name)
click_element :protected_branch_select
click_element :protected_branch_dropdown
within_element(:protected_branch_dropdown) do
within_element(:protected_branch_dropdown_content) do
click_on branch_name
end
end

View File

@ -32,7 +32,7 @@ module QA
end
view 'app/views/projects/_last_push.html.haml' do
element :create_merge_request
element :create_merge_request_button
end
view 'app/views/projects/_home_panel.html.haml' do
@ -54,7 +54,7 @@ module QA
end
view 'app/views/projects/empty.html.haml' do
element :quick_actions
element :quick_actions_container
end
view 'app/assets/javascripts/repository/components/breadcrumbs.vue' do
@ -72,7 +72,7 @@ module QA
end
view 'app/views/projects/blob/viewers/_loading.html.haml' do
element :spinner
element :spinner_placeholder
end
view 'app/views/projects/buttons/_download.html.haml' do
@ -80,11 +80,11 @@ module QA
end
def wait_for_viewers_to_load
has_no_element?(:spinner, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
has_no_element?(:spinner_placeholder, wait: QA::Support::Repeater::DEFAULT_MAX_WAIT_TIME)
end
def create_first_new_file!
within_element(:quick_actions) do
within_element(:quick_actions_container) do
click_link_with_text 'New file'
end
end
@ -122,7 +122,7 @@ module QA
end
def has_create_merge_request_button?
has_css?(element_selector_css(:create_merge_request))
has_css?(element_selector_css(:create_merge_request_button))
end
def has_file?(name)
@ -152,7 +152,7 @@ module QA
has_create_merge_request_button?
end
click_element :create_merge_request
click_element :create_merge_request_button
end
def open_web_ide!

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :runner do
RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do
describe 'Pipeline API defined variable inheritance' do
include_context 'variable inheritance test prep'

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :requires_admin do
RSpec.describe 'Verify', :requires_admin, product_group: :pipeline_execution do
describe 'When user is blocked' do
let!(:admin_api_client) { Runtime::API::Client.as_admin }
let!(:user_api_client) { Runtime::API::Client.new(:gitlab, user: user) }

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :runner, feature_flag: {
RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring, feature_flag: {
name: 'ci_stop_expanding_file_vars_for_runners',
scope: :project
} do

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :runner do
RSpec.describe 'Verify', :runner, product_group: :runner do
describe 'Runner removal' do
include Support::API

View File

@ -2,7 +2,7 @@
module QA
RSpec.describe 'Verify' do
describe 'Add or Remove CI variable via UI', :smoke do
describe 'Add or Remove CI variable via UI', :smoke, product_group: :pipeline_authoring do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project-with-ci-variables'

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :runner do
RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do
describe 'Pipeline with customizable variable' do
let(:executor) { "qa-runner-#{Time.now.to_i}" }
let(:pipeline_job_name) { 'customizable-variable' }

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :runner do
RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do
describe 'Pipeline with protected variable' do
let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
let(:protected_value) { Faker::Alphanumeric.alphanumeric(8) }

View File

@ -2,7 +2,7 @@
module QA
RSpec.describe 'Verify' do
describe 'Pipeline with prefill variables' do
describe 'Pipeline with prefill variables', product_group: :pipeline_authoring do
let(:prefill_variable_description1) { Faker::Lorem.sentence }
let(:prefill_variable_value1) { Faker::Lorem.word }
let(:prefill_variable_description2) { Faker::Lorem.sentence }

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :runner do
RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do
describe 'UI defined variable' do
include_context 'variable inheritance test prep'

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :runner do
RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do
describe 'UI defined variable' do
include_context 'variable inheritance test prep'

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :smoke, :runner, quarantine: {
RSpec.describe 'Verify', :smoke, :runner, product_group: :pipeline_execution, quarantine: {
issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/356295',
type: :investigating
} do

View File

@ -2,7 +2,7 @@
module QA
RSpec.describe 'Verify' do
describe 'Include local config file paths with wildcard', :reliable do
describe 'Include local config file paths with wildcard', :reliable, product_group: :pipeline_authoring do
let(:project) do
Resource::Project.fabricate_via_api! do |project|
project.name = 'project-with-pipeline'

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :runner do
RSpec.describe 'Verify', :runner, product_group: :pipeline_authoring do
describe 'Include multiple files from a project' do
let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }
let(:expected_text) { Faker::Lorem.sentence }

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :runner, :requires_admin do
RSpec.describe 'Verify', :runner, :requires_admin, product_group: :pipeline_insights do
describe 'Artifacts' do
context 'when locked' do
let(:file_name) { 'artifact.txt' }

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
module QA
RSpec.describe 'Verify', :runner do
RSpec.describe 'Verify', :runner, product_group: :pipeline_execution do
context 'When pipeline is blocked' do
let(:executor) { "qa-runner-#{Faker::Alphanumeric.alphanumeric(8)}" }

Some files were not shown because too many files have changed in this diff Show More