Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ $ pip install mkdocs-monorepo-plugin

Take a look in [our sample project](./sample-docs) for an example implementation, or see [what it looks like after running `mkdocs build`](https://backstage.github.io/mkdocs-monorepo-plugin/monorepo-example/).

For further examples, take a look at our [test suite](__tests__/integration/fixtures) which covers dozens of good and bad examples of using Mkdocs with the `monorepo` plugin.

In general, this plugin introduces the `!include` syntax in your Mkdocs navigation structure and then merges them together.

```yaml
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# API Documentation

Some sub documentation
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Other Documentation
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
site_name: "test/api"
site_description: "This is an api."
repo_url: https://github.com/backstage/mkdocs-monorepo-plugin
edit_uri: edit/master/__tests__/integration/fixtures/ok-include-path-edit-uri-nested/api/docs/

plugins:
- monorepo

nav:
- Home: index.md
- Other: other/other.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Top Level Project Documentation

Some higher level info about the project
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Other Documentation
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
site_name: "Example"
site_description: "Description Here"
repo_url: https://github.com/backstage/mkdocs-monorepo-plugin
edit_uri: edit/master/__tests__/integration/fixtures/ok-include-path-edit-uri-nested/docs/

plugins:
- monorepo

nav:
- Home: "index.md"
- Other: "other.md"
- API: "!include api/mkdocs.yml"
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ plugins:

nav:
- Home: index.md
- Section: '!include ../mkdocs.yml'
- Section: '!include ../ok/project-a/mkdocs.yml'
22 changes: 18 additions & 4 deletions __tests__/integration/test.bats
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,15 @@ teardown() {
[[ "$output" == *"This contains a sentence which only exists in the ok-include-wildcard/project-b fixture."* ]]
}

@test "fails if !include path is above current folder" {
cd ${fixturesDir}/error-include-path-is-parent
assertFailedMkdocs build
[[ "$output" == *"[mkdocs-monorepo] The mkdocs file "*"/__tests__/integration/fixtures/mkdocs.yml is outside of the current directory. Please move the file and try again."* ]]
@test "builds a mkdocs site if !include path is outside root" {
cd ${fixturesDir}/ok-include-path-outside-root
assertSuccessMkdocs build

assertFileExists site/index.html
[[ "$output" == *"Lorem markdownum aequora Famemque, a ramos regna Ulixem verba, posito qui
nubilus membra."* ]]
assertFileExists site/test/index.html
[[ "$output" == *"This contains a sentence which only exists in the ok/project-a fixture."* ]]
}

@test "fails if !include path contains !include" {
Expand Down Expand Up @@ -268,6 +273,15 @@ teardown() {
assertFileContains './site/test/other/other/index.html' 'href="https://github.com/backstage/mkdocs-monorepo-plugin/edit/master/__tests__/integration/fixtures/ok-include-path-edit-uri/api/docs/other/other.md"'
}

@test "sets edit url for nested included path pages" {
cd ${fixturesDir}/ok-include-path-edit-uri-nested
assertSuccessMkdocs build

assertFileContains './site/index.html' 'href="https://github.com/backstage/mkdocs-monorepo-plugin/edit/master/__tests__/integration/fixtures/ok-include-path-edit-uri-nested/docs/index.md"'
assertFileContains './site/test/api/index.html' 'href="https://github.com/backstage/mkdocs-monorepo-plugin/edit/master/__tests__/integration/fixtures/ok-include-path-edit-uri-nested/api/docs/index.md"'
assertFileContains './site/test/api/other/other/index.html' 'href="https://github.com/backstage/mkdocs-monorepo-plugin/edit/master/__tests__/integration/fixtures/ok-include-path-edit-uri-nested/api/docs/other/other.md"'
}

@test "sets edit url for included wildcard pages" {
cd ${fixturesDir}/ok-include-wildcard-edit-uri
assertSuccessMkdocs build
Expand Down
9 changes: 9 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 1.1.1

- Removed overly restrictive directory structure. It is now possible to call directories outside of your current directory. [#139]
- Bumped core dependencies
- Avoid replacing dots for underscores [#129]
- Remove use of warning_filter (deprecated) [#108]
- add support for monorepos with symlink folders [#122]
- Fix edit URL for included projects with nested site_name [#128]

## 1.1.0

- Dropped official support for Python 3.7
Expand Down
180 changes: 94 additions & 86 deletions mkdocs_monorepo_plugin/edit_uri.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,94 +12,102 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from mkdocs.utils import yaml_load
from os import path

from mkdocs.utils import yaml_load


class EditUrl:
def __init__(self, config, page, plugin):
self.config = config
self.page = page
self.plugin = plugin

def __get_root_config_file_path(self):
return path.dirname(self.config['config_file_path'])

def __get_root_docs_dir(self):
abs_root_config_file_dir = self.__get_root_config_file_path()
return path.relpath(self.plugin.originalDocsDir, abs_root_config_file_dir)

def __get_page_dir_alias(self):
parts = self.page.url.split('/')
while True:
parts.pop()
alias = path.join(*parts)
if alias in self.plugin.aliases:
return alias

def __get_page_docs_dir(self):
alias = self.__get_page_dir_alias()
abs_root_config_file_dir = self.__get_root_config_file_path()
abs_page_config_file_dir = self.plugin.aliases[alias]['docs_dir']
return path.relpath(abs_page_config_file_dir, abs_root_config_file_dir)

def __get_page_src_path(self):
alias = self.page.url.split('/')[0]
path = self.page.file.src_path
return path.replace('{}/'.format(alias), '')

def __get_page_config_file_path(self):
alias = self.__get_page_dir_alias()
return self.plugin.aliases[alias]['yaml_file']

def __load_page_config_file(self, file):
config = yaml_load(file)

root_docs_dir = self.__get_root_docs_dir()
root_repo_url = self.config.get('repo_url')
root_edit_uri = self.config.get('edit_uri', '') or ''

page_docs_dir = self.__get_page_docs_dir()
page_repo_url = config.get('repo_url', root_repo_url)
page_edit_uri = config.get('edit_uri', root_edit_uri.replace(root_docs_dir, page_docs_dir))

# ensure a well-formed edit_uri
if page_edit_uri:
if not page_edit_uri.startswith(('?', '#')) \
and not page_repo_url.endswith('/'):
page_repo_url += '/'
if not page_edit_uri.endswith('/'):
page_edit_uri += '/'

config['docs_dir'] = page_docs_dir
config['edit_uri'] = page_edit_uri
config['repo_url'] = page_repo_url

return config

def __get_page_config_file_yaml(self):
abs_page_config_file_path = self.__get_page_config_file_path()
with open(abs_page_config_file_path, 'rb') as f:
return self.__load_page_config_file(f)

def __has_repo(self):
page_config_file_yaml = self.__get_page_config_file_yaml()
return bool(page_config_file_yaml.get('repo_url'))

def __is_root(self):
root_config_docs_dir = self.__get_root_docs_dir()
abs_root_config_file_dir = self.__get_root_config_file_path()
abs_root_config_docs_dir = path.join(abs_root_config_file_dir, root_config_docs_dir)

return path.realpath(abs_root_config_docs_dir) in self.page.file.abs_src_path

def build(self):
if self.__is_root():
return self.page.edit_url
if self.__has_repo():
config = self.__get_page_config_file_yaml()
return config['repo_url'] + config['edit_uri'] + self.__get_page_src_path()
return None
def __init__(self, config, page, plugin):
self.config = config
self.page = page
self.plugin = plugin

def __get_root_config_file_path(self):
return path.dirname(self.config["config_file_path"])

def __get_root_docs_dir(self):
abs_root_config_file_dir = self.__get_root_config_file_path()
return path.relpath(self.plugin.originalDocsDir, abs_root_config_file_dir)

def __get_page_dir_alias(self):
parts = self.page.url.split("/")
while True:
parts.pop()
alias = path.join(*parts)
if alias in self.plugin.aliases:
return alias

def __get_page_docs_dir(self):
alias = self.__get_page_dir_alias()
abs_root_config_file_dir = self.__get_root_config_file_path()
abs_page_config_file_dir = self.plugin.aliases[alias]["docs_dir"]
return path.relpath(abs_page_config_file_dir, abs_root_config_file_dir)

def __get_page_src_path(self):
alias = self.__get_page_dir_alias()
path = self.page.file.src_path
return path.replace("{}/".format(alias), "")

def __get_page_config_file_path(self):
alias = self.__get_page_dir_alias()
return self.plugin.aliases[alias]["yaml_file"]

def __load_page_config_file(self, file):
config = yaml_load(file)

root_docs_dir = self.__get_root_docs_dir()
root_repo_url = self.config.get("repo_url")
root_edit_uri = self.config.get("edit_uri", "") or ""

page_docs_dir = self.__get_page_docs_dir()
page_repo_url = config.get("repo_url", root_repo_url)
page_edit_uri = config.get(
"edit_uri", root_edit_uri.replace(root_docs_dir, page_docs_dir)
)

# ensure a well-formed edit_uri
if page_edit_uri:
if not page_edit_uri.startswith(("?", "#")) and not page_repo_url.endswith(
"/"
):
page_repo_url += "/"
if not page_edit_uri.endswith("/"):
page_edit_uri += "/"

config["docs_dir"] = page_docs_dir
config["edit_uri"] = page_edit_uri
config["repo_url"] = page_repo_url

return config

def __get_page_config_file_yaml(self):
abs_page_config_file_path = self.__get_page_config_file_path()
with open(abs_page_config_file_path, "rb") as f:
return self.__load_page_config_file(f)

def __has_repo(self):
page_config_file_yaml = self.__get_page_config_file_yaml()
return bool(page_config_file_yaml.get("repo_url"))

def __is_root(self):
root_config_docs_dir = self.__get_root_docs_dir()
abs_root_config_file_dir = self.__get_root_config_file_path()
abs_root_config_docs_dir = path.join(
abs_root_config_file_dir, root_config_docs_dir
)

return path.realpath(abs_root_config_docs_dir) in self.page.file.abs_src_path

def build(self):
if self.__is_root():
return self.page.edit_url
if self.__has_repo():
config = self.__get_page_config_file_yaml()
return config["repo_url"] + config["edit_uri"] + self.__get_page_src_path()
return None


def set_edit_url(config, page, plugin):
edit_url = EditUrl(config, page, plugin)
page.edit_url = edit_url.build()
edit_url = EditUrl(config, page, plugin)
page.edit_url = edit_url.build()
38 changes: 21 additions & 17 deletions mkdocs_monorepo_plugin/merger.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,45 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from tempfile import TemporaryDirectory
from shutil import copytree

import logging
import os
from os.path import join
from pathlib import Path

from mkdocs.utils import warning_filter
from shutil import copytree
from tempfile import TemporaryDirectory

log = logging.getLogger(__name__)
log.addFilter(warning_filter)

# This collects the multiple docs/ folders and merges them together.


class Merger:
def __init__(self, config):
self.config = config
self.root_docs_dir = config['docs_dir']
self.root_docs_dir = config["docs_dir"]
self.docs_dirs = list()
self.append('', self.root_docs_dir)
self.append("", self.root_docs_dir)
self.files_source_dir = dict()

def append(self, alias, docs_dir):
self.docs_dirs.append([alias, docs_dir])

def merge(self):
self.temp_docs_dir = TemporaryDirectory('', 'docs_')

aliases = list(filter(lambda docs_dir: len(docs_dir) > 0, map(
lambda docs_dir: docs_dir[0], self.docs_dirs)))
self.temp_docs_dir = TemporaryDirectory("", "docs_")

aliases = list(
filter(
lambda docs_dir: len(docs_dir) > 0,
map(lambda docs_dir: docs_dir[0], self.docs_dirs),
)
)
if len(aliases) != len(set(aliases)):
log.critical(
"[mkdocs-monorepo] You cannot have duplicated site names. " +
"Current registered site names in the monorepository: {}".format(', '.join(aliases)))
"[mkdocs-monorepo] You cannot have duplicated site names. "
+ "Current registered site names in the monorepository: {}".format(
", ".join(aliases)
)
)
raise SystemExit(1)

for alias, docs_dir in self.docs_dirs:
Expand All @@ -60,7 +63,7 @@ def merge(self):

if os.path.exists(source_dir):
copytree(source_dir, dest_dir, symlinks=True, dirs_exist_ok=True)
for file_abs_path in Path(source_dir).rglob('*.md'):
for file_abs_path in Path(source_dir).rglob("*.md"):
file_abs_path = str(file_abs_path) # python 3.5 compatibility
if os.path.isfile(file_abs_path):
file_rel_path = os.path.relpath(file_abs_path, source_dir)
Expand All @@ -69,8 +72,9 @@ def merge(self):

else:
log.critical(
"[mkdocs-monorepo] The {} path is not valid. ".format(source_dir) +
"Please update your 'nav' with a valid path.")
"[mkdocs-monorepo] The {} path is not valid. ".format(source_dir)
+ "Please update your 'nav' with a valid path."
)
raise SystemExit(1)

return str(self.temp_docs_dir.name)
Expand Down
Loading
Loading