Commit f9651d0e authored by Stan Hu's avatar Stan Hu

Merge branch 'issue-241744-support-kroki' into 'master'

resolves #241744 add Kroki to support more diagrams in AsciiDoc and Markdown

See merge request gitlab-org/gitlab!44851
parents 44b5330e 6598f992
...@@ -159,6 +159,7 @@ gem 'wikicloth', '0.8.1' ...@@ -159,6 +159,7 @@ gem 'wikicloth', '0.8.1'
gem 'asciidoctor', '~> 2.0.10' gem 'asciidoctor', '~> 2.0.10'
gem 'asciidoctor-include-ext', '~> 0.3.1', require: false gem 'asciidoctor-include-ext', '~> 0.3.1', require: false
gem 'asciidoctor-plantuml', '~> 0.0.12' gem 'asciidoctor-plantuml', '~> 0.0.12'
gem 'asciidoctor-kroki', '~> 0.2.2', require: false
gem 'rouge', '~> 3.25.0' gem 'rouge', '~> 3.25.0'
gem 'truncato', '~> 0.7.11' gem 'truncato', '~> 0.7.11'
gem 'bootstrap_form', '~> 4.2.0' gem 'bootstrap_form', '~> 4.2.0'
......
...@@ -84,6 +84,8 @@ GEM ...@@ -84,6 +84,8 @@ GEM
asciidoctor (2.0.10) asciidoctor (2.0.10)
asciidoctor-include-ext (0.3.1) asciidoctor-include-ext (0.3.1)
asciidoctor (>= 1.5.6, < 3.0.0) asciidoctor (>= 1.5.6, < 3.0.0)
asciidoctor-kroki (0.2.2)
asciidoctor (~> 2.0)
asciidoctor-plantuml (0.0.12) asciidoctor-plantuml (0.0.12)
asciidoctor (>= 1.5.6, < 3.0.0) asciidoctor (>= 1.5.6, < 3.0.0)
ast (2.4.1) ast (2.4.1)
...@@ -1269,6 +1271,7 @@ DEPENDENCIES ...@@ -1269,6 +1271,7 @@ DEPENDENCIES
asana (= 0.10.2) asana (= 0.10.2)
asciidoctor (~> 2.0.10) asciidoctor (~> 2.0.10)
asciidoctor-include-ext (~> 0.3.1) asciidoctor-include-ext (~> 0.3.1)
asciidoctor-kroki (~> 0.2.2)
asciidoctor-plantuml (~> 0.0.12) asciidoctor-plantuml (~> 0.0.12)
atlassian-jwt (~> 0.2.0) atlassian-jwt (~> 0.2.0)
attr_encrypted (~> 3.1.0) attr_encrypted (~> 3.1.0)
......
...@@ -254,6 +254,8 @@ module ApplicationSettingsHelper ...@@ -254,6 +254,8 @@ module ApplicationSettingsHelper
:password_authentication_enabled_for_git, :password_authentication_enabled_for_git,
:performance_bar_allowed_group_path, :performance_bar_allowed_group_path,
:performance_bar_enabled, :performance_bar_enabled,
:kroki_enabled,
:kroki_url,
:plantuml_enabled, :plantuml_enabled,
:plantuml_url, :plantuml_url,
:polling_interval_multiplier, :polling_interval_multiplier,
......
...@@ -13,6 +13,9 @@ class ApplicationSetting < ApplicationRecord ...@@ -13,6 +13,9 @@ class ApplicationSetting < ApplicationRecord
GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \ GRAFANA_URL_ERROR_MESSAGE = 'Please check your Grafana URL setting in ' \
'Admin Area > Settings > Metrics and profiling > Metrics - Grafana' 'Admin Area > Settings > Metrics and profiling > Metrics - Grafana'
KROKI_URL_ERROR_MESSAGE = 'Please check your Kroki URL setting in ' \
'Admin Area > Settings > General > Kroki'
add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption) ? :optional : :required } add_authentication_token_field :runners_registration_token, encrypted: -> { Feature.enabled?(:application_settings_tokens_optional_encryption) ? :optional : :required }
add_authentication_token_field :health_check_access_token add_authentication_token_field :health_check_access_token
add_authentication_token_field :static_objects_external_storage_auth_token add_authentication_token_field :static_objects_external_storage_auth_token
...@@ -128,6 +131,11 @@ class ApplicationSetting < ApplicationRecord ...@@ -128,6 +131,11 @@ class ApplicationSetting < ApplicationRecord
presence: true, presence: true,
if: :unique_ips_limit_enabled if: :unique_ips_limit_enabled
validates :kroki_url,
presence: { if: :kroki_enabled }
validate :validate_kroki_url, if: :kroki_enabled
validates :plantuml_url, validates :plantuml_url,
presence: true, presence: true,
if: :plantuml_enabled if: :plantuml_enabled
...@@ -429,18 +437,21 @@ class ApplicationSetting < ApplicationRecord ...@@ -429,18 +437,21 @@ class ApplicationSetting < ApplicationRecord
after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') } after_commit :expire_performance_bar_allowed_user_ids_cache, if: -> { previous_changes.key?('performance_bar_allowed_group_id') }
def validate_grafana_url def validate_grafana_url
unless parsed_grafana_url validate_url(parsed_grafana_url, :grafana_url, GRAFANA_URL_ERROR_MESSAGE)
self.errors.add(
:grafana_url,
"must be a valid relative or absolute URL. #{GRAFANA_URL_ERROR_MESSAGE}"
)
end
end end
def grafana_url_absolute? def grafana_url_absolute?
parsed_grafana_url&.absolute? parsed_grafana_url&.absolute?
end end
def validate_kroki_url
validate_url(parsed_kroki_url, :kroki_url, KROKI_URL_ERROR_MESSAGE)
end
def kroki_url_absolute?
parsed_kroki_url&.absolute?
end
def sourcegraph_url_is_com? def sourcegraph_url_is_com?
!!(sourcegraph_url =~ /\Ahttps:\/\/(www\.)?sourcegraph\.com/) !!(sourcegraph_url =~ /\Ahttps:\/\/(www\.)?sourcegraph\.com/)
end end
...@@ -503,6 +514,24 @@ class ApplicationSetting < ApplicationRecord ...@@ -503,6 +514,24 @@ class ApplicationSetting < ApplicationRecord
def parsed_grafana_url def parsed_grafana_url
@parsed_grafana_url ||= Gitlab::Utils.parse_url(grafana_url) @parsed_grafana_url ||= Gitlab::Utils.parse_url(grafana_url)
end end
def parsed_kroki_url
@parsed_kroki_url ||= Gitlab::UrlBlocker.validate!(kroki_url, schemes: %w(http https), enforce_sanitization: true)[0]
rescue Gitlab::UrlBlocker::BlockedUrlError => error
self.errors.add(
:kroki_url,
"is not valid. #{error}"
)
end
def validate_url(parsed_url, name, error_message)
unless parsed_url
self.errors.add(
name,
"must be a valid relative or absolute URL. #{error_message}"
)
end
end
end end
ApplicationSetting.prepend_if_ee('EE::ApplicationSetting') ApplicationSetting.prepend_if_ee('EE::ApplicationSetting')
...@@ -168,7 +168,9 @@ module ApplicationSettingImplementation ...@@ -168,7 +168,9 @@ module ApplicationSettingImplementation
user_show_add_ssh_key_message: true, user_show_add_ssh_key_message: true,
wiki_page_max_content_bytes: 50.megabytes, wiki_page_max_content_bytes: 50.megabytes,
container_registry_delete_tags_service_timeout: 250, container_registry_delete_tags_service_timeout: 250,
container_registry_expiration_policies_worker_capacity: 0 container_registry_expiration_policies_worker_capacity: 0,
kroki_enabled: false,
kroki_url: nil
} }
end end
......
- expanded = integration_expanded?('kroki_')
%section.settings.as-kroki.no-animate#js-kroki-settings{ class: ('expanded' if expanded) }
.settings-header
%h4
= _('Kroki')
%button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' }
= expanded ? _('Collapse') : _('Expand')
%p
= _('Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}.').html_safe % { link: link_to('Kroki', 'https://kroki.io', target: '_blank') }
.settings-content
= form_for @application_setting, url: general_admin_application_settings_path(anchor: 'js-kroki-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting) if expanded
%fieldset
.form-group
.form-check
= f.check_box :kroki_enabled, class: 'form-check-input'
= f.label :kroki_enabled, _('Enable Kroki'), class: 'form-check-label'
.form-group
= f.label :kroki_url, 'Kroki URL', class: 'label-bold'
= f.text_field :kroki_url, class: 'form-control', placeholder: 'http://your-kroki-instance:8000'
.form-text.text-muted
= (_('When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you\'ve installed Kroki, make sure to update the server URL to point to your instance.') % { kroki_public_url: '<code>https://kroki.io</code>', install_link: link_to('install Kroki', 'https://docs.kroki.io/kroki/setup/install/', target: '_blank') }).html_safe
= f.submit _('Save changes'), class: "btn gl-button btn-success"
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
= f.label :plantuml_enabled, _('Enable PlantUML'), class: 'form-check-label' = f.label :plantuml_enabled, _('Enable PlantUML'), class: 'form-check-label'
.form-group .form-group
= f.label :plantuml_url, 'PlantUML URL', class: 'label-bold' = f.label :plantuml_url, 'PlantUML URL', class: 'label-bold'
= f.text_field :plantuml_url, class: 'form-control', placeholder: 'http://gitlab.your-plantuml-instance.com:8080' = f.text_field :plantuml_url, class: 'form-control', placeholder: 'http://your-plantuml-instance:8080'
.form-text.text-muted .form-text.text-muted
Allow rendering of Allow rendering of
= link_to "PlantUML", "http://plantuml.com" = link_to "PlantUML", "http://plantuml.com"
......
...@@ -117,6 +117,7 @@ ...@@ -117,6 +117,7 @@
= render_if_exists 'admin/application_settings/elasticsearch_form' = render_if_exists 'admin/application_settings/elasticsearch_form'
= render 'admin/application_settings/gitpod' = render 'admin/application_settings/gitpod'
= render 'admin/application_settings/kroki'
= render 'admin/application_settings/plantuml' = render 'admin/application_settings/plantuml'
= render 'admin/application_settings/sourcegraph' = render 'admin/application_settings/sourcegraph'
= render_if_exists 'admin/application_settings/slack' = render_if_exists 'admin/application_settings/slack'
......
---
title: "Add Kroki to support more diagrams in AsciiDoc and Markdown"
merge_request: 44851
author: Guillaume Grossetie
type: added
# frozen_string_literal: true
class AddKrokiApplicationSettings < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
# rubocop:disable Migration/AddLimitToTextColumns
# limit is added in 20201011005400_add_text_limit_to_application_settings_kroki_url.rb
#
def change
add_column :application_settings, :kroki_url, :text
add_column :application_settings, :kroki_enabled, :boolean, default: false, null: false
end
# rubocop:enable Migration/AddLimitToTextColumns
end
# frozen_string_literal: true
class AddTextLimitToApplicationSettingsKrokiUrl < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_text_limit :application_settings, :kroki_url, 1024
end
def down
# Down is required as `add_text_limit` is not reversible
#
remove_text_limit :application_settings, :kroki_url
end
end
dd2ada53f01debcc91070525e4386db959b91881a8945e9082d0b3318ceb35cf
\ No newline at end of file
07bfc8e9a684ae64b7d78c9d867f9bafebd46678f6f168aa87d2ad7f0e85d75e
\ No newline at end of file
...@@ -9320,6 +9320,8 @@ CREATE TABLE application_settings ( ...@@ -9320,6 +9320,8 @@ CREATE TABLE application_settings (
elasticsearch_indexed_file_size_limit_kb integer DEFAULT 1024 NOT NULL, elasticsearch_indexed_file_size_limit_kb integer DEFAULT 1024 NOT NULL,
enforce_namespace_storage_limit boolean DEFAULT false NOT NULL, enforce_namespace_storage_limit boolean DEFAULT false NOT NULL,
container_registry_delete_tags_service_timeout integer DEFAULT 250 NOT NULL, container_registry_delete_tags_service_timeout integer DEFAULT 250 NOT NULL,
kroki_url character varying,
kroki_enabled boolean,
elasticsearch_client_request_timeout integer DEFAULT 0 NOT NULL, elasticsearch_client_request_timeout integer DEFAULT 0 NOT NULL,
gitpod_enabled boolean DEFAULT false NOT NULL, gitpod_enabled boolean DEFAULT false NOT NULL,
gitpod_url text DEFAULT 'https://gitpod.io/'::text, gitpod_url text DEFAULT 'https://gitpod.io/'::text,
...@@ -9347,6 +9349,7 @@ CREATE TABLE application_settings ( ...@@ -9347,6 +9349,7 @@ CREATE TABLE application_settings (
secret_detection_revocation_token_types_url text, secret_detection_revocation_token_types_url text,
cloud_license_enabled boolean DEFAULT false NOT NULL, cloud_license_enabled boolean DEFAULT false NOT NULL,
CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)), CONSTRAINT app_settings_registry_exp_policies_worker_capacity_positive CHECK ((container_registry_expiration_policies_worker_capacity >= 0)),
CONSTRAINT check_17d9558205 CHECK ((char_length((kroki_url)::text) <= 1024)),
CONSTRAINT check_2dba05b802 CHECK ((char_length(gitpod_url) <= 255)), CONSTRAINT check_2dba05b802 CHECK ((char_length(gitpod_url) <= 255)),
CONSTRAINT check_51700b31b5 CHECK ((char_length(default_branch_name) <= 255)), CONSTRAINT check_51700b31b5 CHECK ((char_length(default_branch_name) <= 255)),
CONSTRAINT check_57123c9593 CHECK ((char_length(help_page_documentation_base_url) <= 255)), CONSTRAINT check_57123c9593 CHECK ((char_length(help_page_documentation_base_url) <= 255)),
......
# Kroki diagrams **(CORE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/241744) in GitLab 13.7.
When [Kroki](https://kroki.io) integration is enabled and configured in
GitLab you can use it to create diagrams in AsciiDoc and Markdown documents.
## Kroki Server
When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images.
You can use the free public cloud instance `https://kroki.io` or you can [install Kroki](https://docs.kroki.io/kroki/setup/install/)
on your own infrastructure.
Once you've installed Kroki, make sure to update the server URL to point to your instance.
### Docker
With Docker, run a container like this:
```shell
docker run -d --name kroki -p 8080:8000 yuzutech/kroki
```
The **Kroki URL** is the hostname of the server running the container.
The [`yuzutech/kroki`](https://hub.docker.com/r/yuzutech/kroki) image contains the following diagrams libraries out-of-the-box:
- [Bytefield](https://bytefield-svg.deepsymmetry.org/)
- [Ditaa](http://ditaa.sourceforge.net)
- [Erd](https://github.com/BurntSushi/erd)
- [GraphViz](https://www.graphviz.org/)
- [Nomnoml](https://github.com/skanaar/nomnoml)
- [PlantUML](https://github.com/plantuml/plantuml)
- [C4 model](https://github.com/RicardoNiepel/C4-PlantUML) (with PlantUML)
- [Svgbob](https://github.com/ivanceras/svgbob)
- [UMlet](https://github.com/umlet/umlet)
- [Vega](https://github.com/vega/vega)
- [Vega-Lite](https://github.com/vega/vega-lite)
- [WaveDrom](https://wavedrom.com/)
If you want to use additional diagram libraries,
read the [Kroki installation](https://docs.kroki.io/kroki/setup/install/#_images) to learn how to start Kroki companion containers.
## Enable Kroki in GitLab **(CORE ONLY)**
You need to enable Kroki integration from Settings under Admin Area.
To do that, log in with an Admin account and follow these steps:
1. Select the Admin Area (**{admin}**) icon.
1. Navigate to **Settings > General**.
1. Expand the **Kroki** section.
1. Select **Enable Kroki** checkbox.
1. Enter the **Kroki URL**.
## Create diagrams
With Kroki integration enabled and configured, you can start adding diagrams to
your AsciiDoc or Markdown documentation using delimited blocks:
- **Markdown**
````markdown
```plantuml
Bob -> Alice : hello
Alice -> Bob : hi
```
````
- **AsciiDoc**
```plaintext
[plantuml]
....
Bob->Alice : hello
Alice -> Bob : hi
....
```
The above blocks are converted to an HTML image tag with source pointing to the
Kroki instance. If the Kroki server is correctly configured, this should
render a nice diagram instead of the block:
![PlantUML diagram](../img/kroki_plantuml_diagram.png)
Kroki supports more than a dozen diagram libraries. Here's a few examples:
**GraphViz**
```plaintext
[graphviz]
....
digraph finite_state_machine {
rankdir=LR;
node [shape = doublecircle]; LR_0 LR_3 LR_4 LR_8;
node [shape = circle];
LR_0 -> LR_2 [ label = "SS(B)" ];
LR_0 -> LR_1 [ label = "SS(S)" ];
LR_1 -> LR_3 [ label = "S($end)" ];
LR_2 -> LR_6 [ label = "SS(b)" ];
LR_2 -> LR_5 [ label = "SS(a)" ];
LR_2 -> LR_4 [ label = "S(A)" ];
LR_5 -> LR_7 [ label = "S(b)" ];
LR_5 -> LR_5 [ label = "S(a)" ];
LR_6 -> LR_6 [ label = "S(b)" ];
LR_6 -> LR_5 [ label = "S(a)" ];
LR_7 -> LR_8 [ label = "S(b)" ];
LR_7 -> LR_5 [ label = "S(a)" ];
LR_8 -> LR_6 [ label = "S(b)" ];
LR_8 -> LR_5 [ label = "S(a)" ];
}
....
```
![GraphViz diagram](../img/kroki_graphviz_diagram.png)
**C4 (based on PlantUML)**
```plaintext
[c4plantuml]
....
@startuml
!include C4_Context.puml
title System Context diagram for Internet Banking System
Person(customer, "Banking Customer", "A customer of the bank, with personal bank accounts.")
System(banking_system, "Internet Banking System", "Allows customers to check their accounts.")
System_Ext(mail_system, "E-mail system", "The internal Microsoft Exchange e-mail system.")
System_Ext(mainframe, "Mainframe Banking System", "Stores all of the core banking information.")
Rel(customer, banking_system, "Uses")
Rel_Back(customer, mail_system, "Sends e-mails to")
Rel_Neighbor(banking_system, mail_system, "Sends e-mails", "SMTP")
Rel(banking_system, mainframe, "Uses")
@enduml
....
```
![C4 PlantUML diagram](../img/kroki_c4_diagram.png)
**Nomnoml**
```plaintext
[nomnoml]
....
[Pirate|eyeCount: Int|raid();pillage()|
[beard]--[parrot]
[beard]-:>[foul mouth]
]
[<abstract>Marauder]<:--[Pirate]
[Pirate]- 0..7[mischief]
[jollyness]->[Pirate]
[jollyness]->[rum]
[jollyness]->[singing]
[Pirate]-> *[rum|tastiness: Int|swig()]
[Pirate]->[singing]
[singing]<->[rum]
....
```
![Nomnoml diagram](../img/kroki_nomnoml_diagram.png)
...@@ -106,6 +106,11 @@ docker.elastic.co/elasticsearch/elasticsearch:5.5.1 ...@@ -106,6 +106,11 @@ docker.elastic.co/elasticsearch/elasticsearch:5.5.1
Then confirm it works in the browser at `curl http://<IP_ADDRESS>:9200/_cat/health`. Then confirm it works in the browser at `curl http://<IP_ADDRESS>:9200/_cat/health`.
Elasticsearch's default username is `elastic` and password is `changeme`. Elasticsearch's default username is `elastic` and password is `changeme`.
### Kroki
See [our Kroki docs](../integration/kroki.md#docker)
on running Kroki in Docker.
### PlantUML ### PlantUML
See [our PlantUML docs](../integration/plantuml.md#docker) See [our PlantUML docs](../integration/plantuml.md#docker)
......
...@@ -57,6 +57,8 @@ Example response: ...@@ -57,6 +57,8 @@ Example response:
"repository_storages_weighted": {"default": 100}, "repository_storages_weighted": {"default": 100},
"plantuml_enabled": false, "plantuml_enabled": false,
"plantuml_url": null, "plantuml_url": null,
"kroki_enabled": false,
"kroki_url": null,
"terminal_max_session_time": 0, "terminal_max_session_time": 0,
"polling_interval_multiplier": 1.0, "polling_interval_multiplier": 1.0,
"rsa_key_restriction": 0, "rsa_key_restriction": 0,
......
...@@ -50,7 +50,8 @@ GitLab can be integrated with the following external service for continuous inte ...@@ -50,7 +50,8 @@ GitLab can be integrated with the following external service for continuous inte
GitLab can be integrated with the following enhancements: GitLab can be integrated with the following enhancements:
- Add GitLab actions to [Gmail actions buttons](gmail_action_buttons_for_gitlab.md). - Add GitLab actions to [Gmail actions buttons](gmail_action_buttons_for_gitlab.md).
- Configure [PlantUML](../administration/integration/plantuml.md) to use diagrams in AsciiDoc documents. - Configure [PlantUML](../administration/integration/plantuml.md)
or [Kroki](../administration/integration/kroki.md) to use diagrams in AsciiDoc and Markdown documents.
- Attach merge requests to [Trello](trello_power_up.md) cards. - Attach merge requests to [Trello](trello_power_up.md) cards.
- Enable integrated code intelligence powered by [Sourcegraph](sourcegraph.md). - Enable integrated code intelligence powered by [Sourcegraph](sourcegraph.md).
- Add [Elasticsearch](elasticsearch.md) for [Advanced Search](../user/search/advanced_global_search.md), - Add [Elasticsearch](elasticsearch.md) for [Advanced Search](../user/search/advanced_global_search.md),
......
...@@ -34,7 +34,8 @@ Access the default page for admin area settings by navigating to **Admin Area > ...@@ -34,7 +34,8 @@ Access the default page for admin area settings by navigating to **Admin Area >
| Option | Description | | Option | Description |
| ------ | ----------- | | ------ | ----------- |
| [Elasticsearch](../../../integration/elasticsearch.md#enabling-advanced-search) | Elasticsearch integration. Elasticsearch AWS IAM. | | [Elasticsearch](../../../integration/elasticsearch.md#enabling-advanced-search) | Elasticsearch integration. Elasticsearch AWS IAM. |
| [PlantUML](../../../administration/integration/plantuml.md#gitlab) | Allow rendering of PlantUML diagrams in AsciiDoc documents. | | [Kroki](../../../administration/integration/kroki.md#enable-kroki-in-gitlab) | Allow rendering of diagrams in AsciiDoc and Markdown documents using [kroki.io](https://kroki.io). |
| [PlantUML](../../../administration/integration/plantuml.md#gitlab) | Allow rendering of PlantUML diagrams in AsciiDoc and Markdown documents. |
| [Slack application](../../../user/project/integrations/gitlab_slack_application.md#configuration) **(FREE ONLY)** | Slack integration allows you to interact with GitLab via slash commands in a chat window. This option is only available on GitLab.com, though it may be [available for self-managed instances in the future](https://gitlab.com/gitlab-org/gitlab/-/issues/28164). | | [Slack application](../../../user/project/integrations/gitlab_slack_application.md#configuration) **(FREE ONLY)** | Slack integration allows you to interact with GitLab via slash commands in a chat window. This option is only available on GitLab.com, though it may be [available for self-managed instances in the future](https://gitlab.com/gitlab-org/gitlab/-/issues/28164). |
| [Third party offers](third_party_offers.md) | Control the display of third party offers. | | [Third party offers](third_party_offers.md) | Control the display of third party offers. |
| [Snowplow](../../../development/product_analytics/snowplow.md) | Configure the Snowplow integration. | | [Snowplow](../../../development/product_analytics/snowplow.md) | Configure the Snowplow integration. |
......
...@@ -440,6 +440,36 @@ graph LR ...@@ -440,6 +440,36 @@ graph LR
---- ----
``` ```
#### Kroki
Kroki supports more than a dozen diagram libraries.
To make Kroki available in GitLab, a GitLab administrator needs to enable it first.
Read more in the [Kroki integration](../administration/integration/kroki.md) page.
Once Kroki is enabled, you can create a wide variety of diagrams in AsciiDoc and Markdown documents.
Here's an example using a GraphViz diagram:
**AsciiDoc**
```plaintext
[graphviz]
....
digraph G {
Hello->World
}
....
```
**Markdown**
````markdown
```graphviz
digraph G {
Hello->World
}
```
````
#### PlantUML #### PlantUML
To make PlantUML available in GitLab, a GitLab administrator needs to enable it first. To make PlantUML available in GitLab, a GitLab administrator needs to enable it first.
......
...@@ -162,6 +162,7 @@ Color written inside backticks is followed by a color "chip": ...@@ -162,6 +162,7 @@ Color written inside backticks is followed by a color "chip":
### Diagrams and flowcharts ### Diagrams and flowcharts
It's possible to generate diagrams and flowcharts from text in GitLab using [Mermaid](https://mermaidjs.github.io/) or [PlantUML](https://plantuml.com). It's possible to generate diagrams and flowcharts from text in GitLab using [Mermaid](https://mermaidjs.github.io/) or [PlantUML](https://plantuml.com).
It's also possible to use [Kroki](https://kroki.io) to create a wide variety of diagrams.
#### Mermaid #### Mermaid
...@@ -231,6 +232,11 @@ end ...@@ -231,6 +232,11 @@ end
To make PlantUML available in GitLab, a GitLab administrator needs to enable it first. Read more in [PlantUML & GitLab](../administration/integration/plantuml.md). To make PlantUML available in GitLab, a GitLab administrator needs to enable it first. Read more in [PlantUML & GitLab](../administration/integration/plantuml.md).
#### Kroki
To make Kroki available in GitLab, a GitLab administrator needs to enable it first.
Read more in the [Kroki integration](../administration/integration/kroki.md) page.
### Emoji ### Emoji
If this section is not rendered correctly, [view it in GitLab](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/markdown.md#emoji). If this section is not rendered correctly, [view it in GitLab](https://gitlab.com/gitlab-org/gitlab/blob/master/doc/user/markdown.md#emoji).
......
...@@ -102,6 +102,10 @@ module API ...@@ -102,6 +102,10 @@ module API
optional :performance_bar_allowed_group_id, type: String, desc: 'Deprecated: Use :performance_bar_allowed_group_path instead. Path of the group that is allowed to toggle the performance bar.' # support legacy names, can be removed in v6 optional :performance_bar_allowed_group_id, type: String, desc: 'Deprecated: Use :performance_bar_allowed_group_path instead. Path of the group that is allowed to toggle the performance bar.' # support legacy names, can be removed in v6
optional :performance_bar_allowed_group_path, type: String, desc: 'Path of the group that is allowed to toggle the performance bar.' optional :performance_bar_allowed_group_path, type: String, desc: 'Path of the group that is allowed to toggle the performance bar.'
optional :performance_bar_enabled, type: String, desc: 'Deprecated: Pass `performance_bar_allowed_group_path: nil` instead. Allow enabling the performance.' # support legacy names, can be removed in v6 optional :performance_bar_enabled, type: String, desc: 'Deprecated: Pass `performance_bar_allowed_group_path: nil` instead. Allow enabling the performance.' # support legacy names, can be removed in v6
optional :kroki_enabled, type: Boolean, desc: 'Enable Kroki'
given kroki_enabled: ->(val) { val } do
requires :kroki_url, type: String, desc: 'The Kroki server URL'
end
optional :plantuml_enabled, type: Boolean, desc: 'Enable PlantUML' optional :plantuml_enabled, type: Boolean, desc: 'Enable PlantUML'
given plantuml_enabled: ->(val) { val } do given plantuml_enabled: ->(val) { val } do
requires :plantuml_url, type: String, desc: 'The PlantUML server URL' requires :plantuml_url, type: String, desc: 'The PlantUML server URL'
......
...@@ -25,7 +25,7 @@ module Banzai ...@@ -25,7 +25,7 @@ module Banzai
# Allow data-math-style attribute in order to support LaTeX formatting # Allow data-math-style attribute in order to support LaTeX formatting
whitelist[:attributes]['code'] = %w(data-math-style) whitelist[:attributes]['code'] = %w(data-math-style)
whitelist[:attributes]['pre'] = %w(data-math-style data-mermaid-style) whitelist[:attributes]['pre'] = %w(data-math-style data-mermaid-style data-kroki-style)
# Allow html5 details/summary elements # Allow html5 details/summary elements
whitelist[:elements].push('details') whitelist[:elements].push('details')
......
# frozen_string_literal: true
require "nokogiri"
require "asciidoctor/extensions/asciidoctor_kroki/extension"
module Banzai
module Filter
# HTML that replaces all diagrams supported by Kroki with the corresponding img tags.
#
class KrokiFilter < HTML::Pipeline::Filter
def call
return doc unless settings.kroki_enabled
diagram_selectors = ::Gitlab::Kroki.formats(settings)
.map { |diagram_type| %(pre[lang="#{diagram_type}"] > code) }
.join(', ')
return doc unless doc.at(diagram_selectors)
diagram_format = "svg"
doc.css(diagram_selectors).each do |node|
diagram_type = node.parent['lang']
img_tag = Nokogiri::HTML::DocumentFragment.parse(%(<img src="#{create_image_src(diagram_type, diagram_format, node.content)}"/>))
node.parent.replace(img_tag)
end
doc
end
private
def create_image_src(type, format, text)
::AsciidoctorExtensions::KrokiDiagram.new(type, format, text)
.get_diagram_uri(settings.kroki_url)
end
def settings
Gitlab::CurrentSettings.current_application_settings
end
end
end
end
# frozen_string_literal: true # frozen_string_literal: true
require 'rouge/plugins/common_mark' require 'rouge/plugins/common_mark'
require "asciidoctor/extensions/asciidoctor_kroki/extension"
# Generated HTML is transformed back to GFM by app/assets/javascripts/behaviors/markdown/nodes/code_block.js # Generated HTML is transformed back to GFM by app/assets/javascripts/behaviors/markdown/nodes/code_block.js
module Banzai module Banzai
...@@ -14,7 +15,7 @@ module Banzai ...@@ -14,7 +15,7 @@ module Banzai
LANG_PARAMS_ATTR = 'data-lang-params' LANG_PARAMS_ATTR = 'data-lang-params'
def call def call
doc.search('pre:not([data-math-style]):not([data-mermaid-style]) > code').each do |node| doc.search('pre:not([data-math-style]):not([data-mermaid-style]):not([data-kroki-style]) > code').each do |node|
highlight_node(node) highlight_node(node)
end end
...@@ -86,7 +87,7 @@ module Banzai ...@@ -86,7 +87,7 @@ module Banzai
end end
def use_rouge?(language) def use_rouge?(language)
%w(math mermaid plantuml suggestion).exclude?(language) (%w(math suggestion) + ::AsciidoctorExtensions::Kroki::SUPPORTED_DIAGRAM_NAMES).exclude?(language)
end end
end end
end end
......
...@@ -19,6 +19,7 @@ module Banzai ...@@ -19,6 +19,7 @@ module Banzai
Filter::SyntaxHighlightFilter, Filter::SyntaxHighlightFilter,
Filter::MathFilter, Filter::MathFilter,
Filter::ColorFilter, Filter::ColorFilter,
Filter::KrokiFilter,
Filter::MermaidFilter, Filter::MermaidFilter,
Filter::VideoLinkFilter, Filter::VideoLinkFilter,
Filter::AudioLinkFilter, Filter::AudioLinkFilter,
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
require 'asciidoctor' require 'asciidoctor'
require 'asciidoctor-plantuml' require 'asciidoctor-plantuml'
require 'asciidoctor/extensions/asciidoctor_kroki/extension'
require 'asciidoctor/extensions' require 'asciidoctor/extensions'
require 'gitlab/asciidoc/html5_converter' require 'gitlab/asciidoc/html5_converter'
require 'gitlab/asciidoc/mermaid_block_processor' require 'gitlab/asciidoc/mermaid_block_processor'
...@@ -23,7 +24,14 @@ module Gitlab ...@@ -23,7 +24,14 @@ module Gitlab
'source-highlighter' => 'gitlab-html-pipeline', 'source-highlighter' => 'gitlab-html-pipeline',
'icons' => 'font', 'icons' => 'font',
'outfilesuffix' => '.adoc', 'outfilesuffix' => '.adoc',
'max-include-depth' => MAX_INCLUDE_DEPTH 'max-include-depth' => MAX_INCLUDE_DEPTH,
# This feature is disabled because it relies on File#read to read the file.
# If we want to enable this feature we will need to provide a "GitLab compatible" implementation.
# This attribute is typically used to share common config (skinparam...) across all PlantUML diagrams.
# The value can be a path or a URL.
'kroki-plantuml-include!' => '',
# This feature is disabled because it relies on the local file system to save diagrams retrieved from the Kroki server.
'kroki-fetch-diagram!' => ''
}.freeze }.freeze
def self.path_attrs(path) def self.path_attrs(path)
...@@ -48,12 +56,21 @@ module Gitlab ...@@ -48,12 +56,21 @@ module Gitlab
extensions = proc do extensions = proc do
include_processor ::Gitlab::Asciidoc::IncludeProcessor.new(context) include_processor ::Gitlab::Asciidoc::IncludeProcessor.new(context)
block ::Gitlab::Asciidoc::MermaidBlockProcessor block ::Gitlab::Asciidoc::MermaidBlockProcessor
::Gitlab::Kroki.formats(Gitlab::CurrentSettings).each do |name|
block ::AsciidoctorExtensions::KrokiBlockProcessor, name
end
end end
extra_attrs = path_attrs(context[:requested_path]) extra_attrs = path_attrs(context[:requested_path])
asciidoc_opts = { safe: :secure, asciidoc_opts = { safe: :secure,
backend: :gitlab_html5, backend: :gitlab_html5,
attributes: DEFAULT_ADOC_ATTRS.merge(extra_attrs), attributes: DEFAULT_ADOC_ATTRS
.merge(extra_attrs)
.merge({
# Define the Kroki server URL from the settings.
# This attribute cannot be overridden from the AsciiDoc document.
'kroki-server-url' => Gitlab::CurrentSettings.kroki_url
}),
extensions: extensions } extensions: extensions }
context[:pipeline] = :ascii_doc context[:pipeline] = :ascii_doc
......
# frozen_string_literal: true
require 'asciidoctor/extensions/asciidoctor_kroki/extension'
module Gitlab
# Helper methods for Kroki
module Kroki
BLOCKDIAG_FORMATS = %w[
blockdiag
seqdiag
actdiag
nwdiag
packetdiag
rackdiag
].freeze
# Diagrams that require a companion container are disabled for now
DIAGRAMS_FORMATS = ::AsciidoctorExtensions::Kroki::SUPPORTED_DIAGRAM_NAMES
.reject { |diagram_type| diagram_type == 'mermaid' || diagram_type == 'bpmn' || BLOCKDIAG_FORMATS.include?(diagram_type) }
DIAGRAMS_FORMATS_WO_PLANTUML = DIAGRAMS_FORMATS
.reject { |diagram_type| diagram_type == 'plantuml' }
# Get the list of diagram formats that are currently enabled
#
# Returns an Array of diagram formats.
# If Kroki is not enabled, returns an empty Array.
def self.formats(current_settings)
return [] unless current_settings.kroki_enabled
# If PlantUML is enabled, PlantUML diagrams will be processed by the PlantUML server.
# In other words, the PlantUML server has precedence over Kroki since both can process PlantUML diagrams.
if current_settings.plantuml_enabled
DIAGRAMS_FORMATS_WO_PLANTUML
else
DIAGRAMS_FORMATS
end
end
end
end
...@@ -2834,6 +2834,9 @@ msgstr "" ...@@ -2834,6 +2834,9 @@ msgstr ""
msgid "Allow rendering of PlantUML diagrams in Asciidoc documents." msgid "Allow rendering of PlantUML diagrams in Asciidoc documents."
msgstr "" msgstr ""
msgid "Allow rendering of diagrams in AsciiDoc and Markdown documents using %{link}."
msgstr ""
msgid "Allow repository mirroring to be configured by project maintainers" msgid "Allow repository mirroring to be configured by project maintainers"
msgstr "" msgstr ""
...@@ -10217,6 +10220,9 @@ msgstr "" ...@@ -10217,6 +10220,9 @@ msgstr ""
msgid "Enable Incident Management inbound alert limit" msgid "Enable Incident Management inbound alert limit"
msgstr "" msgstr ""
msgid "Enable Kroki"
msgstr ""
msgid "Enable PlantUML" msgid "Enable PlantUML"
msgstr "" msgstr ""
...@@ -15635,6 +15641,9 @@ msgstr "" ...@@ -15635,6 +15641,9 @@ msgstr ""
msgid "Ki" msgid "Ki"
msgstr "" msgstr ""
msgid "Kroki"
msgstr ""
msgid "Kubernetes" msgid "Kubernetes"
msgstr "" msgstr ""
...@@ -30580,6 +30589,9 @@ msgstr "" ...@@ -30580,6 +30589,9 @@ msgstr ""
msgid "What’s your experience level?" msgid "What’s your experience level?"
msgstr "" msgstr ""
msgid "When Kroki is enabled, GitLab sends diagrams to an instance of Kroki to display them as images. You can use the free public cloud instance %{kroki_public_url} or you can %{install_link} on your own infrastructure. Once you've installed Kroki, make sure to update the server URL to point to your instance."
msgstr ""
msgid "When a deployment job is successful, skip older deployment jobs that are still pending" msgid "When a deployment job is successful, skip older deployment jobs that are still pending"
msgstr "" msgstr ""
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Banzai::Filter::KrokiFilter do
include FilterSpecHelper
it 'replaces nomnoml pre tag with img tag if kroki is enabled' do
stub_application_setting(kroki_enabled: true, kroki_url: "http://localhost:8000")
doc = filter("<pre lang='nomnoml'><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]</code></pre>")
expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==">'
end
it 'replaces nomnoml pre tag with img tag if both kroki and plantuml are enabled' do
stub_application_setting(kroki_enabled: true,
kroki_url: "http://localhost:8000",
plantuml_enabled: true,
plantuml_url: "http://localhost:8080")
doc = filter("<pre lang='nomnoml'><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]</code></pre>")
expect(doc.to_s).to eq '<img src="http://localhost:8000/nomnoml/svg/eNqLDsgsSixJrUmtTHXOL80rsVLwzCupKUrMTNHQtC7IzMlJTE_V0KzhUlCITkpNLEqJ1dWNLkgsKsoviUUSs7KLTssvzVHIzS8tyYjligUAMhEd0g==">'
end
it 'does not replace nomnoml pre tag with img tag if kroki is disabled' do
stub_application_setting(kroki_enabled: false)
doc = filter("<pre lang='nomnoml'><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:>[foul mouth]\n]</code></pre>")
expect(doc.to_s).to eq "<pre lang=\"nomnoml\"><code>[Pirate|eyeCount: Int|raid();pillage()|\n [beard]--[parrot]\n [beard]-:&gt;[foul mouth]\n]</code></pre>"
end
it 'does not replace plantuml pre tag with img tag if both kroki and plantuml are enabled' do
stub_application_setting(kroki_enabled: true,
kroki_url: "http://localhost:8000",
plantuml_enabled: true,
plantuml_url: "http://localhost:8080")
doc = filter("<pre lang='plantuml'><code>Bob->Alice : hello</code></pre>")
expect(doc.to_s).to eq '<pre lang="plantuml"><code>Bob-&gt;Alice : hello</code></pre>'
end
end
...@@ -20,7 +20,7 @@ module Gitlab ...@@ -20,7 +20,7 @@ module Gitlab
expected_asciidoc_opts = { expected_asciidoc_opts = {
safe: :secure, safe: :secure,
backend: :gitlab_html5, backend: :gitlab_html5,
attributes: described_class::DEFAULT_ADOC_ATTRS, attributes: described_class::DEFAULT_ADOC_ATTRS.merge({ "kroki-server-url" => nil }),
extensions: be_a(Proc) extensions: be_a(Proc)
} }
...@@ -35,7 +35,7 @@ module Gitlab ...@@ -35,7 +35,7 @@ module Gitlab
expected_asciidoc_opts = { expected_asciidoc_opts = {
safe: :secure, safe: :secure,
backend: :gitlab_html5, backend: :gitlab_html5,
attributes: described_class::DEFAULT_ADOC_ATTRS, attributes: described_class::DEFAULT_ADOC_ATTRS.merge({ "kroki-server-url" => nil }),
extensions: be_a(Proc) extensions: be_a(Proc)
} }
...@@ -462,6 +462,34 @@ module Gitlab ...@@ -462,6 +462,34 @@ module Gitlab
expect(render(input, context)).to include(output.strip) expect(render(input, context)).to include(output.strip)
end end
end end
context 'with Kroki enabled' do
before do
allow_any_instance_of(ApplicationSetting).to receive(:kroki_enabled).and_return(true)
allow_any_instance_of(ApplicationSetting).to receive(:kroki_url).and_return('https://kroki.io')
end
it 'converts a graphviz diagram to image' do
input = <<~ADOC
[graphviz]
....
digraph G {
Hello->World
}
....
ADOC
output = <<~HTML
<div>
<div>
<a class="no-attachment-icon" href="https://kroki.io/graphviz/svg/eNpLyUwvSizIUHBXqOZSUPBIzcnJ17ULzy_KSeGqBQCEzQka" target="_blank" rel="noopener noreferrer"><img src="data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" alt="Diagram" class="lazy" data-src="https://kroki.io/graphviz/svg/eNpLyUwvSizIUHBXqOZSUPBIzcnJ17ULzy_KSeGqBQCEzQka"></a>
</div>
</div>
HTML
expect(render(input, context)).to include(output.strip)
end
end
end end
context 'with project' do context 'with project' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment