Skip to content

Commit

Permalink
fix: Use relative path to create GitHub links, fix regex (#37)
Browse files Browse the repository at this point in the history
When checking whether a file has an existing Confluence page, and when linking to GitHub, use the file path relative to the repo root and not relative to the root of the machine that is running the wiki sync script.

Also update the regex that finds links, so that multiple links on the same line are parsed properly.
  • Loading branch information
KevinGDialpad authored Nov 17, 2022
1 parent e2821a7 commit 9f509cb
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 26 deletions.
94 changes: 76 additions & 18 deletions tests/test_relative_links.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ def test_http_link(wiki_mock, get_repo_root_mock):
print('Check out this [link](https://example.org)', file=doc_file)

output = wiki_sync.get_formatted_file_content(
wiki_mock, doc_path, GH_ROOT, REPO_NAME)
wiki_mock, repo_root, doc_path, GH_ROOT, REPO_NAME)

assert output == 'Check out this [link|https://example.org]\n'


def test_link_to_file_in_same_folder(wiki_mock, get_repo_root_mock):
def test_link_to_file_both_in_root(wiki_mock, get_repo_root_mock):
with tempfile.TemporaryDirectory() as repo_root:
get_repo_root_mock.return_value = repo_root

Expand All @@ -62,10 +62,34 @@ def test_link_to_file_in_same_folder(wiki_mock, get_repo_root_mock):
print(contents, file=doc_file)

output = wiki_sync.get_formatted_file_content(
wiki_mock, doc_path, GH_ROOT, REPO_NAME)
wiki_mock, repo_root, doc_path, GH_ROOT, REPO_NAME)

expected_output = (f'Check out this'
f' [other file|{GH_ROOT}{linked_doc_path}]\n')
expected_gh_link = f'{GH_ROOT}{linked_file_name}'
expected_output = f'Check out this [other file|{expected_gh_link}]\n'
assert output == expected_output


def test_link_to_file_in_same_non_root_folder(wiki_mock, get_repo_root_mock):
with tempfile.TemporaryDirectory() as repo_root:
get_repo_root_mock.return_value = repo_root

os.makedirs(os.path.join(repo_root, 'foo'))
# Create a file that the doc will link to
linked_file_name = 'linked_file.py'
linked_doc_path = os.path.join(repo_root, 'foo', linked_file_name)
write_something_to_file(linked_doc_path)

# Create the doc file with a link to the other one
doc_path = os.path.join(repo_root, 'foo', 'new_doc.md')
with open(doc_path, mode='w', encoding='utf-8') as doc_file:
contents = f'Check out this [other file]({linked_file_name})'
print(contents, file=doc_file)

output = wiki_sync.get_formatted_file_content(
wiki_mock, repo_root, doc_path, GH_ROOT, REPO_NAME)

expected_gh_link = f'{GH_ROOT}foo/{linked_file_name}'
expected_output = f'Check out this [other file|{expected_gh_link}]\n'
assert output == expected_output


Expand All @@ -86,10 +110,10 @@ def test_link_to_file_in_child_folder(wiki_mock, get_repo_root_mock):
print(contents, file=doc_file)

output = wiki_sync.get_formatted_file_content(
wiki_mock, doc_path, GH_ROOT, REPO_NAME)
wiki_mock, repo_root, doc_path, GH_ROOT, REPO_NAME)

expected_output = (f'Check out this'
f' [other file|{GH_ROOT}{linked_doc_path}]\n')
expected_gh_link = f'{GH_ROOT}foo/bar/linked_file.py'
expected_output = f'Check out this [other file|{expected_gh_link}]\n'
assert output == expected_output


Expand All @@ -110,10 +134,10 @@ def test_link_to_file_in_parent_folder(wiki_mock, get_repo_root_mock):
print(contents, file=doc_file)

output = wiki_sync.get_formatted_file_content(
wiki_mock, doc_path, GH_ROOT, REPO_NAME)
wiki_mock, repo_root, doc_path, GH_ROOT, REPO_NAME)

expected_output = (f'Check out this'
f' [other file|{GH_ROOT}{linked_doc_path}]\n')
expected_gh_link = f'{GH_ROOT}linked_file.py'
expected_output = f'Check out this [other file|{expected_gh_link}]\n'
assert output == expected_output


Expand All @@ -135,9 +159,9 @@ def test_simplified_link(wiki_mock, get_repo_root_mock):
print(contents, file=doc_file)

output = wiki_sync.get_formatted_file_content(
wiki_mock, doc_path, GH_ROOT, REPO_NAME)
wiki_mock, repo_root, doc_path, GH_ROOT, REPO_NAME)

expected_link = f'[{linked_file_name}|{GH_ROOT}{linked_doc_path}'
expected_link = f'[{linked_file_name}|{GH_ROOT}linked_file.py'
assert output == f'Check out {expected_link}\n'


Expand All @@ -152,13 +176,17 @@ def test_link_to_non_existing_file(wiki_mock, get_repo_root_mock):
print(contents, file=doc_file)

output = wiki_sync.get_formatted_file_content(
wiki_mock, doc_path, GH_ROOT, REPO_NAME)
wiki_mock, repo_root, doc_path, GH_ROOT, REPO_NAME)

# Output is the same
assert output == 'Check out this [other file|non_existing.py]\n'


def test_link_to_file_that_exists_on_confluence(wiki_mock, get_repo_root_mock):
os.environ['INPUT_WIKI-BASE-URL'] = 'http://mywiki.atlassian.net'
space = 'WikiSpace'
os.environ['INPUT_SPACE-NAME'] = space
wiki_url = 'http://mywiki.atlassian.net'
os.environ['INPUT_WIKI-BASE-URL'] = wiki_url

with tempfile.TemporaryDirectory() as repo_root:
get_repo_root_mock.return_value = repo_root
Expand All @@ -172,7 +200,7 @@ def test_link_to_file_that_exists_on_confluence(wiki_mock, get_repo_root_mock):
# existing Confluence page, say yes
wiki_mock.get_page_by_title.return_value = {
'_links': {
'webui': '/spaces/SPACE/pages/123'
'webui': f'/spaces/{space}/pages/123'
}
}

Expand All @@ -183,12 +211,42 @@ def test_link_to_file_that_exists_on_confluence(wiki_mock, get_repo_root_mock):
print(contents, file=doc_file)

output = wiki_sync.get_formatted_file_content(
wiki_mock, doc_path, GH_ROOT, REPO_NAME)
wiki_mock, repo_root, doc_path, GH_ROOT, REPO_NAME)

wiki_link = 'http://mywiki.atlassian.net/wiki/spaces/SPACE/pages/123'
wiki_link = f'{wiki_url}/wiki/spaces/{space}/pages/123'
expected_output = (f'Check out this [other file|{wiki_link}]\n')
assert output == expected_output

wiki_mock.get_page_by_title.assert_called_once_with(
space, f'{REPO_NAME}/linked_file.py')


def test_several_links_on_same_line(wiki_mock, get_repo_root_mock):
with tempfile.TemporaryDirectory() as repo_root:
get_repo_root_mock.return_value = repo_root

# Create file that the doc will link to
linked_file_name = 'linked_file.py'
write_something_to_file(os.path.join(repo_root, linked_file_name))
linked_file_name_2 = 'linked_file_2.go'
write_something_to_file(os.path.join(repo_root, linked_file_name_2))

# Create the doc file with a link to the other one
doc_path = os.path.join(repo_root, 'new_doc.md')
with open(doc_path, mode='w', encoding='utf-8') as doc_file:
contents = (f'Check out this [file]({linked_file_name})'
f' and also [that one]({linked_file_name_2})')
print(contents, file=doc_file)

output = wiki_sync.get_formatted_file_content(
wiki_mock, repo_root, doc_path, GH_ROOT, REPO_NAME)

expected_gh_links = [f'{GH_ROOT}{linked_file_name}',
f'{GH_ROOT}{linked_file_name_2}']
expected_output = (f'Check out this [file|{expected_gh_links[0]}] and'
f' also [that one|{expected_gh_links[1]}]\n')
assert output == expected_output


def write_something_to_file(file_path: str) -> None:
with open(file_path, mode='w', encoding='utf-8') as doc_file:
Expand Down
20 changes: 12 additions & 8 deletions wiki_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# We only need a capture group for the link itself
# TODO handle links like [link], which happen when the link name is the same as
# the link itself
JIRA_LINK_PATTERN = re.compile(r'\[.*\|(.*)\]')
JIRA_LINK_PATTERN = re.compile(r'\[[^|\n]+\|([^|\n]+)\]')


def get_files_to_sync(changed_files: str) -> List[str]:
Expand Down Expand Up @@ -91,7 +91,7 @@ def sync_files(files: List[str]) -> bool:

try:
formatted_content = get_formatted_file_content(
wiki_client, absolute_file_path, url_root_for_file,
wiki_client, repo_root, file_path, url_root_for_file,
repo_name)
content = read_only_warning + formatted_content
except Exception:
Expand All @@ -111,8 +111,8 @@ def sync_files(files: List[str]) -> bool:


def get_formatted_file_content(wiki_client: atlassian.Confluence,
file_path: str, gh_root: str, repo_name: str
) -> str:
repo_root: str, file_path: str, gh_root: str,
repo_name: str) -> str:
"""
Takes the absolute path of a file and returns its contents formatted as
JIRA markdown.
Expand All @@ -123,20 +123,24 @@ def get_formatted_file_content(wiki_client: atlassian.Confluence,
# keys are relative links; values are what they should be replaced with
links_to_replace: Dict[str, str] = {}

formated_file_contents = pypandoc.convert_file(file_path, 'jira')
absolute_file_path = os.path.join(repo_root, file_path)
formated_file_contents = pypandoc.convert_file(absolute_file_path, 'jira')

for link in re.findall(JIRA_LINK_PATTERN, formated_file_contents):
# Most links are HTTP - don't waste time with them
if link.startswith('http'):
continue

target_path = os.path.join(os.path.split(file_path)[0], link)
target_path = os.path.join(os.path.split(absolute_file_path)[0], link)
target_path = os.path.normpath(target_path)
if not os.path.exists(target_path): # Not actually a relative link
continue

target_from_root = os.path.relpath(target_path, start=repo_root)

wiki_page_info = wiki_client.get_page_by_title(
os.environ['INPUT_SPACE-NAME'], f'{repo_name}/{target_path}')
os.environ['INPUT_SPACE-NAME'],
f'{repo_name}/{target_from_root}')
if wiki_page_info:
# The link is to a file that has a Confluence page
# Let's link to the page directly
Expand All @@ -145,7 +149,7 @@ def get_formatted_file_content(wiki_client: atlassian.Confluence,
links_to_replace[link] = target_page_url
else:
# No existing Confluence page - link to GitHub
links_to_replace[link] = gh_root + target_path
links_to_replace[link] = gh_root + target_from_root

# Replace relative links
for relative_link, new_link in links_to_replace.items():
Expand Down

0 comments on commit 9f509cb

Please sign in to comment.