Create pipeline/filter converting jira markdown to GitLab Flavored Markdown
What does this MR do?
Assuming we can use the Jira V3 API to get text fields (like description), then we can use the Atlassian Document Format (#212295 (comment 341265335))
Various ADF links:
- Atlassian Document Format
- Document Builder
- Document Viewer
- Jira V3 REST API
- The Atlassian Document Format is Coming to an API Near You
Related to #212295 (closed)
Various sample sources
Jira wiki markdown source
This is a complex issue…and this is normal text
----
Color - Dark Gray
Color - {color:#97a0af}Light Gray{color}
Color - {color:#6554c0}Purple{color}
Color - {color:#00b8d9}Teal{color}
Color - {color:#36b37e}Green{color}
Color - {color:#ff5630}Red{color}
Color - {color:#ff991f}Orange{color}
Color - {color:red}Red specified by name{color}
----
[https://gitlab-jira.atlassian.net/browse/DEMO-1|https://gitlab-jira.atlassian.net/browse/DEMO-1|smart-link]
[External Link|https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25718]
----
{quote}This is a block quote{quote}
{panel:bgColor=#e3fcef}
Success info panel
{panel}
{panel:bgColor=#deebff}
Info info panel
{panel}
{panel:bgColor=#eae6ff}
Note info panel
{panel}
{panel:bgColor=#fffae6}
Warning info panel
{panel}
{panel:bgColor=#ffebe6}
Error info panel
{panel}
----
[~accountid:5e32f803e127810e82875bc1] what up
😀 🤣 :partying_face: 😍
||*Col 1 Row 1*||*Col 2 Row 1*||*Col 3 Row 1*||
|Col 1 Row 2|Col 2 Row 2|Col 3 Row 2|
|Col 1 Row 3|Col 2 Row 3|Col 3 Row 3|
h1. Header 1
h2. Header 2
h3. Header 3
h4. Header 4
h5. Header 5
h6. Header 6
* Bullet point list item 1
* Bullet point list Item 2
* Bullet point list Item 3
# Number list Item 1
# Number list item 2
# Number list item 3
+Underline+
^Superscript^
~Subscript~
*Bold*
_Italic_
-Strikethrough-
{code:javascript}
export function makeIssue({ parentIssue, project, users }) {
const issueType = pickRandom(project.issueTypes)
let data = {
fields: {
summary: faker.lorem.sentence(),
issuetype: {
id: issueType.id
},
project: {
id: project.id
},
reporter: {
id: pickRandom(users)
}
}
}
if (issueType.subtask) {
data = {
parent: {
key: parentIssue
}
}
}
console.log(data)
return data
}{code}
!love-pokemon-pretty|width=400,height=400!
!tree-swing.jpeg|width=1280,height=789!
!import-jira-issues.png|width=1280,height=598!
!tree-swing.jpeg!
blob:[https://gitlab-jira.atlassian.net/5eb8e93b-7b15-446f-82d9-9d82ad7b8ea5#media-blob-url=true&id=572b2c1b-1b38-44ba-904a-649ee1861917&collection=upload-user-collection-426749591&contextId=10042&mimeType=image%2Fpng&name=import-jira-issues.png&size=294408|https://gitlab-jira.atlassian.net/5eb8e93b-7b15-446f-82d9-9d82ad7b8ea5#media-blob-url=true&id=572b2c1b-1b38-44ba-904a-649ee1861917&collection=upload-user-collection-426749591&contextId=10042&mimeType=image%2Fpng&name=import-jira-issues.png&size=294408]
Run pandoc on it
pandoc --from=jira --to=commonmark
and you get this
Converted to CommonMark using Pandoc
This is a complex issue…and this is normal text
-----
Color - Dark Gray
Color - {color:\#97a0af}Light Gray{color}
Color - {color:\#6554c0}Purple{color}
Color - {color:\#00b8d9}Teal{color}
Color - {color:\#36b37e}Green{color}
Color - {color:\#ff5630}Red{color}
Color - {color:\#ff991f}Orange{color}
Color - <span color="red">Red specified by name</span>
-----
\[<https://gitlab-jira.atlassian.net/browse/DEMO-1>|<https://gitlab-jira.atlassian.net/browse/DEMO-1>|smart-link\]
[External
Link](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25718)
-----
> This is a block quote
<div data-bgColor="#e3fcef">
Success info panel
</div>
<div data-bgColor="#deebff">
Info info panel
</div>
<div data-bgColor="#eae6ff">
Note info panel
</div>
<div data-bgColor="#fffae6">
Warning info panel
</div>
<div data-bgColor="#ffebe6">
Error info panel
</div>
-----
\[\~accountid:5e32f803e127810e82875bc1\] what up
😀 🤣 :partying\_face: 😍
<table>
<thead>
<tr class="header">
<th><p><strong>Col 1 Row 1</strong></p></th>
<th><p><strong>Col 2 Row 1</strong></p></th>
<th><p><strong>Col 3 Row 1</strong></p></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><p>Col 1 Row 2</p></td>
<td><p>Col 2 Row 2</p></td>
<td><p>Col 3 Row 2</p></td>
</tr>
<tr class="even">
<td><p>Col 1 Row 3</p></td>
<td><p>Col 2 Row 3</p></td>
<td><p>Col 3 Row 3</p></td>
</tr>
</tbody>
</table>
# Header 1
## Header 2
### Header 3
#### Header 4
##### Header 5
###### Header 6
- Bullet point list item 1
- Bullet point list Item 2
- Bullet point list Item 3
<!-- end list -->
1. Number list Item 1
2. Number list item 2
3. Number list item 3
<span class="inserted">Underline</span>
<sup>Superscript</sup>
<sub>Subscript</sub>
**Bold**
*Italic*
<s>Strikethrough</s>
``` javascript
export function makeIssue({ parentIssue, project, users }) {
const issueType = pickRandom(project.issueTypes)
let data = {
fields: {
summary: faker.lorem.sentence(),
issuetype: {
id: issueType.id
},
project: {
id: project.id
},
reporter: {
id: pickRandom(users)
}
}
}
if (issueType.subtask) {
data = {
parent: {
key: parentIssue
}
}
}
console.log(data)
return data
}
```
\!love-pokemon-pretty|width=400,height=400\!
\!tree-swing.jpeg|width=1280,height=789\!
\!import-jira-issues.png|width=1280,height=598\!
![](tree-swing.jpeg)
blob:<https://gitlab-jira.atlassian.net/5eb8e93b-7b15-446f-82d9-9d82ad7b8ea5#media-blob-url=true&id=572b2c1b-1b38-44ba-904a-649ee1861917&collection=upload-user-collection-426749591&contextId=10042&mimeType=image%2Fpng&name=import-jira-issues.png&size=294408>
which renders like this
GitLab rendered converted CommonMark
This is a complex issue…and this is normal text
Color - Dark Gray
Color - {color:#97a0af}Light Gray{color}
Color - {color:#6554c0}Purple{color}
Color - {color:#00b8d9}Teal{color}
Color - {color:#36b37e}Green{color}
Color - {color:#ff5630}Red{color}
Color - {color:#ff991f}Orange{color}
Color - Red specified by name
[https://gitlab-jira.atlassian.net/browse/DEMO-1|https://gitlab-jira.atlassian.net/browse/DEMO-1|smart-link]
This is a block quote
Success info panel
Info info panel
Note info panel
Warning info panel
Error info panel
[~accountid:5e32f803e127810e82875bc1] what up
Col 1 Row 1 |
Col 2 Row 1 |
Col 3 Row 1 |
---|---|---|
Col 1 Row 2 |
Col 2 Row 2 |
Col 3 Row 2 |
Col 1 Row 3 |
Col 2 Row 3 |
Col 3 Row 3 |
Header 1
Header 2
Header 3
Header 4
Header 5
Header 6
-
Bullet point list item 1
-
Bullet point list Item 2
-
Bullet point list Item 3
-
Number list Item 1
-
Number list item 2
-
Number list item 3
Underline
Superscript
Subscript
Bold
Italic
Strikethrough
export function makeIssue({ parentIssue, project, users }) {
const issueType = pickRandom(project.issueTypes)
let data = {
fields: {
summary: faker.lorem.sentence(),
issuetype: {
id: issueType.id
},
project: {
id: project.id
},
reporter: {
id: pickRandom(users)
}
}
}
if (issueType.subtask) {
data = {
parent: {
key: parentIssue
}
}
}
console.log(data)
return data
}
!love-pokemon-pretty|width=400,height=400!
!tree-swing.jpeg|width=1280,height=789!
!import-jira-issues.png|width=1280,height=598!
And here is the Atlassian Document Format version:
Atlassian Document Format source
"description": {
"version": 1,
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "This is a complex issue…and this is normal text"
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "rule"
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Color - Dark Gray"
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Color - "
},
{
"type": "text",
"text": "Light Gray",
"marks": [
{
"type": "textColor",
"attrs": {
"color": "#97a0af"
}
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Color - "
},
{
"type": "text",
"text": "Purple",
"marks": [
{
"type": "textColor",
"attrs": {
"color": "#6554c0"
}
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Color - "
},
{
"type": "text",
"text": "Teal",
"marks": [
{
"type": "textColor",
"attrs": {
"color": "#00b8d9"
}
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Color - "
},
{
"type": "text",
"text": "Green",
"marks": [
{
"type": "textColor",
"attrs": {
"color": "#36b37e"
}
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Color - "
},
{
"type": "text",
"text": "Red",
"marks": [
{
"type": "textColor",
"attrs": {
"color": "#ff5630"
}
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Color - "
},
{
"type": "text",
"text": "Orange",
"marks": [
{
"type": "textColor",
"attrs": {
"color": "#ff991f"
}
}
]
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "rule"
},
{
"type": "paragraph",
"content": [
{
"type": "inlineCard",
"attrs": {
"url": "https://gitlab-jira.atlassian.net/browse/DEMO-1"
}
},
{
"type": "text",
"text": " "
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "External Link",
"marks": [
{
"type": "link",
"attrs": {
"href": "https://gitlab.com/gitlab-org/gitlab/-/merge_requests/25718"
}
}
]
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "rule"
},
{
"type": "paragraph",
"content": []
},
{
"type": "blockquote",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "This is a block quote"
}
]
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "panel",
"attrs": {
"panelType": "success"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Success info panel"
}
]
}
]
},
{
"type": "panel",
"attrs": {
"panelType": "info"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Info info panel"
}
]
}
]
},
{
"type": "panel",
"attrs": {
"panelType": "note"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Note info panel"
}
]
}
]
},
{
"type": "panel",
"attrs": {
"panelType": "warning"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Warning info panel"
}
]
}
]
},
{
"type": "panel",
"attrs": {
"panelType": "error"
},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Error info panel"
}
]
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "paragraph",
"content": []
},
{
"type": "rule"
},
{
"type": "paragraph",
"content": [
{
"type": "mention",
"attrs": {
"id": "5e32f803e127810e82875bc1",
"text": "jhope"
}
},
{
"type": "text",
"text": " what up"
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "paragraph",
"content": [
{
"type": "emoji",
"attrs": {
"shortName": ":grinning:",
"id": "1f600",
"text": "\uD83D\uDE00"
}
},
{
"type": "text",
"text": " "
},
{
"type": "emoji",
"attrs": {
"shortName": ":rofl:",
"id": "1f923",
"text": "\uD83E\uDD23"
}
},
{
"type": "text",
"text": " "
},
{
"type": "emoji",
"attrs": {
"shortName": ":partying_face:",
"id": "1f973",
"text": "\uD83E\uDD73"
}
},
{
"type": "text",
"text": " "
},
{
"type": "emoji",
"attrs": {
"shortName": ":heart_eyes:",
"id": "1f60d",
"text": "\uD83D\uDE0D"
}
},
{
"type": "text",
"text": " "
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "table",
"attrs": {
"isNumberColumnEnabled": false,
"layout": "default"
},
"content": [
{
"type": "tableRow",
"content": [
{
"type": "tableHeader",
"attrs": {},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Col 1 Row 1",
"marks": [
{
"type": "strong"
}
]
}
]
}
]
},
{
"type": "tableHeader",
"attrs": {},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Col 2 Row 1",
"marks": [
{
"type": "strong"
}
]
}
]
}
]
},
{
"type": "tableHeader",
"attrs": {},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Col 3 Row 1",
"marks": [
{
"type": "strong"
}
]
}
]
}
]
}
]
},
{
"type": "tableRow",
"content": [
{
"type": "tableCell",
"attrs": {},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Col 1 Row 2"
}
]
}
]
},
{
"type": "tableCell",
"attrs": {},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Col 2 Row 2"
}
]
}
]
},
{
"type": "tableCell",
"attrs": {},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Col 3 Row 2"
}
]
}
]
}
]
},
{
"type": "tableRow",
"content": [
{
"type": "tableCell",
"attrs": {},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Col 1 Row 3"
}
]
}
]
},
{
"type": "tableCell",
"attrs": {},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Col 2 Row 3"
}
]
}
]
},
{
"type": "tableCell",
"attrs": {},
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Col 3 Row 3"
}
]
}
]
}
]
}
]
},
{
"type": "heading",
"attrs": {
"level": 1
},
"content": [
{
"type": "text",
"text": "Header 1"
}
]
},
{
"type": "heading",
"attrs": {
"level": 2
},
"content": [
{
"type": "text",
"text": "Header 2"
}
]
},
{
"type": "heading",
"attrs": {
"level": 3
},
"content": [
{
"type": "text",
"text": "Header 3"
}
]
},
{
"type": "heading",
"attrs": {
"level": 4
},
"content": [
{
"type": "text",
"text": "Header 4"
}
]
},
{
"type": "heading",
"attrs": {
"level": 5
},
"content": [
{
"type": "text",
"text": "Header 5"
}
]
},
{
"type": "heading",
"attrs": {
"level": 6
},
"content": [
{
"type": "text",
"text": "Header 6"
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "bulletList",
"content": [
{
"type": "listItem",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Bullet point list item 1"
}
]
}
]
},
{
"type": "listItem",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Bullet point list Item 2"
}
]
}
]
},
{
"type": "listItem",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Bullet point list Item 3"
}
]
}
]
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "orderedList",
"content": [
{
"type": "listItem",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Number list Item 1"
}
]
}
]
},
{
"type": "listItem",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Number list item 2"
}
]
}
]
},
{
"type": "listItem",
"content": [
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Number list item 3"
}
]
}
]
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Underline",
"marks": [
{
"type": "underline"
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Superscript",
"marks": [
{
"type": "subsup",
"attrs": {
"type": "sup"
}
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Subscript",
"marks": [
{
"type": "subsup",
"attrs": {
"type": "sub"
}
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Bold",
"marks": [
{
"type": "strong"
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Italic",
"marks": [
{
"type": "em"
}
]
}
]
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "Strikethrough",
"marks": [
{
"type": "strike"
}
]
}
]
},
{
"type": "codeBlock",
"attrs": {
"language": "javascript"
},
"content": [
{
"type": "text",
"text": "export function makeIssue({ parentIssue, project, users }) {\n \n const issueType = pickRandom(project.issueTypes)\n\n let data = {\n fields: {\n summary: faker.lorem.sentence(),\n issuetype: {\n id: issueType.id\n },\n project: {\n id: project.id\n },\n reporter: {\n id: pickRandom(users)\n }\n }\n }\n\n if (issueType.subtask) {\n data = {\n parent: {\n key: parentIssue\n }\n }\n }\n\n console.log(data)\n\n return data\n}"
}
]
},
{
"type": "mediaSingle",
"attrs": {
"layout": "center"
},
"content": [
{
"type": "media",
"attrs": {
"id": "79411c6b-50e0-477f-b4ed-ac3a5887750c",
"type": "file",
"collection": "jira-10050-field-description",
"width": 400,
"height": 400
}
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "mediaSingle",
"attrs": {
"layout": "center"
},
"content": [
{
"type": "media",
"attrs": {
"id": "6a5b48c6-70bd-4747-9ac8-a9abc9adb1f4",
"type": "file",
"collection": "jira-10050-field-description",
"width": 1280,
"height": 789
}
}
]
},
{
"type": "mediaSingle",
"attrs": {
"layout": "center"
},
"content": [
{
"type": "media",
"attrs": {
"id": "e818a88d-9185-4a7f-8882-18339a0f0966",
"type": "file",
"collection": "jira-10050-field-description",
"width": 1280,
"height": 598
}
}
]
},
{
"type": "paragraph",
"content": []
},
{
"type": "paragraph",
"content": []
},
{
"type": "paragraph",
"content": [
{
"type": "text",
"text": "blob:"
},
{
"type": "text",
"text": "https://gitlab-jira.atlassian.net/5eb8e93b-7b15-446f-82d9-9d82ad7b8ea5#media-blob-url=true&id=572b2c1b-1b38-44ba-904a-649ee1861917&collection=upload-user-collection-426749591&contextId=10042&mimeType=image%2Fpng&name=import-jira-issues.png&size=294408",
"marks": [
{
"type": "link",
"attrs": {
"href": "https://gitlab-jira.atlassian.net/5eb8e93b-7b15-446f-82d9-9d82ad7b8ea5#media-blob-url=true&id=572b2c1b-1b38-44ba-904a-649ee1861917&collection=upload-user-collection-426749591&contextId=10042&mimeType=image%2Fpng&name=import-jira-issues.png&size=294408"
}
}
]
}
]
},
{
"type": "paragraph",
"content": []
}
]
}
Screenshots
Does this MR meet the acceptance criteria?
Conformity
-
Changelog entry -
Documentation (if required) -
Code review guidelines -
Merge request performance guidelines -
Style guides -
Database guides -
Separation of EE specific content
Availability and Testing
-
Review and add/update tests for this feature/bug. Consider all test levels. See the Test Planning Process. -
Tested in all supported browsers -
Informed Infrastructure department of a default or new setting change, if applicable per definition of done
Security
If this MR contains changes to processing or storing of credentials or tokens, authorization and authentication methods and other items described in the security review guidelines:
-
Label as security and @ mention @gitlab-com/gl-security/appsec
-
The MR includes necessary changes to maintain consistency between UI, API, email, or other methods -
Security reports checked/validated by a reviewer from the AppSec team