Skip to content

Commit b89ec57

Browse files
authored
Ensure site_url and use_directory_urls do not conflict.
The site_url config option is now required. If it is set to an empty string, then use_directory_urls will be forced to false. Each will issue a warning if not properly set. In a future release we may raise an error instead. Fixes mkdocs#2189.
1 parent e2a3480 commit b89ec57

12 files changed

Lines changed: 234 additions & 46 deletions

File tree

docs/about/release-notes.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,16 @@ configuration documentation for details.
120120

121121
### Backward Incompatible Changes in 1.2
122122

123+
The [site_url](../user-guide/configuration.md#site_url) configuration option is
124+
now **required**. If it is not set, a warning will be issued. In a future
125+
release an error will be raised (#2189).
126+
127+
The [use_directory_urls](../user-guide/configuration.md#use_directory_urls)
128+
configuration option will be forced to `false` if
129+
[site_url](../user-guide/configuration.md#site_url) is set to an emtpy string.
130+
If `use_direcotry_urls` is not explicitly set to `false` a warning will be
131+
issued (#2189).
132+
123133
A theme's files are now excluded from the list of watched files by default
124134
when using the `--livereload` server. This new default behavior is what most
125135
users need and provides better performance when editing site content.

docs/user-guide/configuration.md

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,16 @@ variable.
6565

6666
### site_url
6767

68-
Set the canonical URL of the site. This will add a link tag with the canonical
69-
URL to the generated HTML header.
68+
Set the canonical URL of the site. This is a **required setting**. If the
69+
'root' of the MkDocs site will be within a subdirectory of a domain, be sure to
70+
include that subdirectory in the setting (`https://example.com/foo/`). If the
71+
domain is yet to be determined, you may use a placeholder domain, which will
72+
need to be updated prior to deployment.
7073

71-
**default**: `null`
74+
If the built site will not be behind a server, then you may set the value to an
75+
empty string (`''`). When set to an empty string, some features of MkDocs may
76+
act differently. For example, the [use_directory_urls](#use_direcotry_urls)
77+
setting must be set to `false`.
7278

7379
### repo_url
7480

@@ -411,10 +417,11 @@ about/license.md | /about/license/ | /about/license.html
411417
The default style of `use_directory_urls: true` creates more user friendly URLs,
412418
and is usually what you'll want to use.
413419

414-
The alternate style can occasionally be useful if you want your documentation to
415-
remain properly linked when opening pages directly from the file system, because
416-
it creates links that point directly to the target *file* rather than the target
417-
*directory*.
420+
The alternate style can be useful if you want your documentation to remain
421+
properly linked when opening pages directly from the file system, because it
422+
creates links that point directly to the target *file* rather than the target
423+
*directory*. In fact, this setting **must be** `false` if [site_url](#site_url)
424+
is set to an emtpy string.
418425

419426
**default**: `true`
420427

mkdocs/commands/new.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import logging
22
import os
33

4-
config_text = 'site_name: My Docs\n'
4+
config_text = """site_name: My Docs
5+
site_url: https://example.com/
6+
"""
57
index_text = """# Welcome to MkDocs
68
79
For full documentation visit [mkdocs.org](https://www.mkdocs.org).

mkdocs/config/config_options.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,15 @@ def __init__(self, default='', required=False, is_dir=False):
270270
self.is_dir = is_dir
271271
super().__init__(default, required)
272272

273+
def pre_validation(self, config, key_name):
274+
# TODO: replace this with an error in a future release (1.3?)
275+
user_defined_keys = sum([list(x.keys()) for x in config.user_configs], [])
276+
if key_name == 'site_url' and key_name not in user_defined_keys:
277+
self.warnings.append(
278+
'This option is now required. Set to a valid URL or '
279+
'an empty string to avoid an error in a future release.'
280+
)
281+
273282
def run_validation(self, value):
274283
if value == '':
275284
return value
@@ -287,6 +296,16 @@ def run_validation(self, value):
287296
raise ValidationError(
288297
"The URL isn't valid, it should include the http:// (scheme)")
289298

299+
def post_validation(self, config, key_name):
300+
if key_name == 'site_url':
301+
if config[key_name] in ['', None] and config['use_directory_urls']:
302+
config['use_directory_urls'] = False
303+
self.warnings.append(
304+
"The 'use_directory_urls' option has been disabled because "
305+
"'site_url' contains an empty value. Either define a valid "
306+
"URL for 'site_url' or set 'use_directory_urls' to False."
307+
)
308+
290309

291310
class RepoURL(URL):
292311
"""

mkdocs/config/defaults.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def get_schema():
2424
('pages', config_options.Nav()),
2525

2626
# The full URL to where the documentation will be hosted
27-
('site_url', config_options.URL(is_dir=True)),
27+
('site_url', config_options.URL(is_dir=True, required=True)),
2828

2929
# A description for the documentation project that will be added to the
3030
# HTML meta tags.

mkdocs/tests/base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from tempfile import TemporaryDirectory
66

77
from mkdocs import config
8+
from mkdocs.config.defaults import get_schema
89
from mkdocs import utils
910

1011

@@ -27,12 +28,14 @@ def load_config(**cfg):
2728
cfg = cfg or {}
2829
if 'site_name' not in cfg:
2930
cfg['site_name'] = 'Example'
31+
if 'site_url' not in cfg:
32+
cfg['site_url'] = 'https://example.com'
3033
if 'config_file_path' not in cfg:
3134
cfg['config_file_path'] = os.path.join(path_base, 'mkdocs.yml')
3235
if 'docs_dir' not in cfg:
3336
# Point to an actual dir to avoid a 'does not exist' error on validation.
3437
cfg['docs_dir'] = os.path.join(path_base, 'docs')
35-
conf = config.Config(schema=config.defaults.get_schema(), config_file_path=cfg['config_file_path'])
38+
conf = config.Config(schema=get_schema(), config_file_path=cfg['config_file_path'])
3639
conf.load_dict(cfg)
3740

3841
errors_warnings = conf.validate()

mkdocs/tests/config/base_tests.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,11 @@ def test_unrecognised_keys(self):
1919

2020
failed, warnings = c.validate()
2121

22-
self.assertEqual(warnings, [
22+
self.assertIn(
2323
('not_a_valid_config_option',
24-
'Unrecognised configuration name: not_a_valid_config_option')
25-
])
24+
'Unrecognised configuration name: not_a_valid_config_option'),
25+
warnings
26+
)
2627

2728
def test_missing_required(self):
2829

@@ -34,7 +35,7 @@ def test_missing_required(self):
3435
self.assertEqual(errors[0][0], 'site_name')
3536
self.assertEqual(str(errors[0][1]), 'Required configuration not provided.')
3637

37-
self.assertEqual(len(warnings), 0)
38+
self.assertEqual(len(warnings), 1)
3839

3940
def test_load_from_file(self):
4041
"""

mkdocs/tests/config/config_tests.py

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def load_missing_config():
2323

2424
def test_missing_site_name(self):
2525
c = config.Config(schema=defaults.get_schema())
26-
c.load_dict({})
26+
c.load_dict({'site_url': 'https://example.com'})
2727
errors, warnings = c.validate()
2828
self.assertEqual(len(errors), 1)
2929
self.assertEqual(errors[0][0], 'site_name')
@@ -290,3 +290,123 @@ def testConfigInstancesUnique(self):
290290
conf.load_dict({'site_name': 'foo'})
291291
conf.validate()
292292
self.assertIsNone(conf['mdx_configs'].get('toc'))
293+
294+
def test_site_url_and_use_directory_urls_undefined(self):
295+
conf = config.Config(schema=defaults.get_schema())
296+
conf.load_dict({
297+
'site_name': 'Example',
298+
'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
299+
})
300+
errors, warnings = conf.validate()
301+
self.assertEqual(conf['site_url'], '')
302+
self.assertFalse(conf['use_directory_urls'])
303+
self.assertEqual(len(warnings), 2)
304+
self.assertEqual(len(errors), 0)
305+
306+
def test_site_url_undefined_and_use_directory_urls_false(self):
307+
conf = config.Config(schema=defaults.get_schema())
308+
conf.load_dict({
309+
'site_name': 'Example',
310+
'use_directory_urls': False,
311+
'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
312+
})
313+
errors, warnings = conf.validate()
314+
self.assertEqual(conf['site_url'], '')
315+
self.assertFalse(conf['use_directory_urls'])
316+
self.assertEqual(len(warnings), 1)
317+
self.assertEqual(len(errors), 0)
318+
319+
def test_site_url_undefined_and_use_directory_urls_true(self):
320+
conf = config.Config(schema=defaults.get_schema())
321+
conf.load_dict({
322+
'site_name': 'Example',
323+
'use_directory_urls': True,
324+
'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
325+
})
326+
errors, warnings = conf.validate()
327+
self.assertEqual(conf['site_url'], '')
328+
self.assertFalse(conf['use_directory_urls'])
329+
self.assertEqual(len(warnings), 2)
330+
self.assertEqual(len(errors), 0)
331+
332+
def test_site_url_empty_and_use_directory_urls_undefined(self):
333+
conf = config.Config(schema=defaults.get_schema())
334+
conf.load_dict({
335+
'site_name': 'Example',
336+
'site_url': '',
337+
'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
338+
})
339+
errors, warnings = conf.validate()
340+
self.assertEqual(conf['site_url'], '')
341+
self.assertFalse(conf['use_directory_urls'])
342+
self.assertEqual(len(warnings), 1)
343+
self.assertEqual(len(errors), 0)
344+
345+
def test_site_url_empty_and_use_directory_urls_false(self):
346+
conf = config.Config(schema=defaults.get_schema())
347+
conf.load_dict({
348+
'site_name': 'Example',
349+
'site_url': '',
350+
'use_directory_urls': False,
351+
'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
352+
})
353+
errors, warnings = conf.validate()
354+
self.assertEqual(conf['site_url'], '')
355+
self.assertFalse(conf['use_directory_urls'])
356+
self.assertEqual(len(warnings), 0)
357+
self.assertEqual(len(errors), 0)
358+
359+
def test_site_url_empty_and_use_directory_urls_true(self):
360+
conf = config.Config(schema=defaults.get_schema())
361+
conf.load_dict({
362+
'site_name': 'Example',
363+
'site_url': '',
364+
'use_directory_urls': True,
365+
'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
366+
})
367+
errors, warnings = conf.validate()
368+
self.assertEqual(conf['site_url'], '')
369+
self.assertFalse(conf['use_directory_urls'])
370+
self.assertEqual(len(warnings), 1)
371+
self.assertEqual(len(errors), 0)
372+
373+
def test_site_url_defined_and_use_directory_urls_undefined(self):
374+
conf = config.Config(schema=defaults.get_schema())
375+
conf.load_dict({
376+
'site_name': 'Example',
377+
'site_url': 'http://example.com/',
378+
'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
379+
})
380+
errors, warnings = conf.validate()
381+
self.assertEqual(conf['site_url'], 'http://example.com/')
382+
self.assertTrue(conf['use_directory_urls'])
383+
self.assertEqual(len(warnings), 0)
384+
self.assertEqual(len(errors), 0)
385+
386+
def test_site_url_defined_and_use_directory_urls_false(self):
387+
conf = config.Config(schema=defaults.get_schema())
388+
conf.load_dict({
389+
'site_name': 'Example',
390+
'site_url': 'http://example.com/',
391+
'use_directory_urls': False,
392+
'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
393+
})
394+
errors, warnings = conf.validate()
395+
self.assertEqual(conf['site_url'], 'http://example.com/')
396+
self.assertFalse(conf['use_directory_urls'])
397+
self.assertEqual(len(warnings), 0)
398+
self.assertEqual(len(errors), 0)
399+
400+
def test_site_url_defined_and_use_directory_urls_true(self):
401+
conf = config.Config(schema=defaults.get_schema())
402+
conf.load_dict({
403+
'site_name': 'Example',
404+
'site_url': 'http://example.com/',
405+
'use_directory_urls': True,
406+
'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml')
407+
})
408+
errors, warnings = conf.validate()
409+
self.assertEqual(conf['site_url'], 'http://example.com/')
410+
self.assertTrue(conf['use_directory_urls'])
411+
self.assertEqual(len(warnings), 0)
412+
self.assertEqual(len(errors), 0)

mkdocs/tests/integration/minimal/mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
site_name: MyTest
2+
site_url: 'https://example.com'
23

34
nav:
45
- 'testing.md'
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
site_name: My Docs
2+
site_url: 'https://example.com'

0 commit comments

Comments
 (0)