[IMP] redirects: split redirects.txt into multiple, per-version, files

The `redirects.txt` file used to specify redirect rules for renamed or
moved documentation pages is starting to grow too big to be easily
maintainable. The main reason is that the number of redirect rules that
were thought to be required has been largely underestimated when
implementing the 'redirects' extension. At first, we believed that no
guidelines or structure were necessary because only a small amount of
redirect rules would be specified. This proved wrong and the file is now
becoming a mess, making it increasingly difficult to figure out where,
why, when, and if a redirect rule is specified in the file.

As the file is versioned, another issue emerges: conflicts occur every
time a commit is forward-ported to a later version if that commit adds a
redirect rule at the end of the file or at a line that was changed in
the later version. As redirect rules are frequently added, and since
blocks of redirect rules for new versions are added at the end of the
file, this tends to happen a lot.

This commit attempts to hit two birds with one stone by splitting the
`redirects.txt` file into multiple files, one per version. While doing
so, the existing redirect rules are ordered alphabetically and moved
into contextual blocks. Instructions and guidelines on how to create
redirect rules are also listed in the `redirects/MANUAL.md` file. By
sorting the redirect rules and adding them in different files, the
number of conflicts should decrease by a lot.

task-2891912

closes odoo/documentation#2292

X-original-commit: 0417b95514
Signed-off-by: Antoine Vandevenne (anv) <anv@odoo.com>
This commit is contained in:
Antoine Vandevenne (anv)
2022-06-22 12:07:05 +00:00
parent 239fc190a0
commit 302b5307fa
14 changed files with 370 additions and 295 deletions

View File

@@ -11,56 +11,60 @@ TEMPLATE = '<html><head><meta http-equiv="refresh" content="0; url=%s"/></head><
logger = logging.getLogger(__name__)
def generate_redirects(app):
path = os.path.join(app.confdir, app.config.redirects_file)
if not os.path.exists(path):
logger.warning("Could not find redirects file at '%s'", path)
return
source_suffix = next(iter(app.config.source_suffix))
def generate_redirects(app):
redirects_dir = Path(app.confdir, app.config.redirects_dir)
if not redirects_dir.exists():
logger.warning("Could not find redirects dir at '%s'", redirects_dir)
return
if not type(app.builder) == builders.StandaloneHTMLBuilder:
logger.info("Redirects are only supported by the 'html' builder. Skipping...")
return
with open(path) as redirects:
escaped_source_suffix = source_suffix.replace('.', '\.')
pattern = re.compile(
r'^[ \t]*([\w\-/]+{0})[ \t]+([\w\-/]+{0})[ \t]*(#.*)?$'.format(escaped_source_suffix)
)
for line in redirects.readlines():
# Exclude comment or empty lines
if not line.rstrip() or line.startswith('#'):
continue
source_suffix = next(iter(app.config.source_suffix))
escaped_source_suffix = source_suffix.replace('.', '\.')
pattern = re.compile(
r'^[ \t]*([\w\-/]+{0})[ \t]+([\w\-/]+{0})[ \t]*(?:#.*)?$'.format(escaped_source_suffix)
)
for redirects_file in redirects_dir.iterdir():
if redirects_file.is_dir() or redirects_file.suffix != '.txt':
continue
match_result = pattern.match(line)
with redirects_file.open(mode='r') as f:
for line in f.readlines():
# Exclude comment or empty lines.
if not line.rstrip() or line.startswith('#'):
continue
# Log malformed rules
if not match_result:
logger.error(
"Ignoring malformed redirection: {0}"
"Please use this format: old_page{1} new_page{1} # optional comment".format(
line, source_suffix)
)
continue
# Match a redirect rule in the line.
match_result = pattern.match(line)
# Parse the rule
from_file, to_file, _ = match_result.groups()
logger.debug("Redirecting '%s' to '%s'", from_file, to_file)
# Exclude malformed rules.
if not match_result:
logger.error(
"Ignoring malformed redirect rule: %s\n"
"See redirects/MANUAL.md to learn how to make redirect rules.", line
)
continue
# Prepare source and destination paths
to_path_prefix = '../' * from_file.count('/')
from_html_file = from_file.replace(source_suffix, '.html')
to_html_file = to_path_prefix + to_file.replace(source_suffix, '.html')
absolute_from_path = Path(app.builder.outdir) / from_html_file
# Parse the rule.
from_file, to_file = match_result.groups()
logger.debug("Redirecting '%s' to '%s'", from_file, to_file)
# Create the redirection
absolute_from_path.parent.mkdir(parents=True, exist_ok=True)
absolute_from_path.write_text(TEMPLATE % to_html_file)
# Prepare the source and destination paths.
to_path_prefix = '../' * from_file.count('/')
from_html_file = from_file.replace(source_suffix, '.html')
to_html_file = to_path_prefix + to_file.replace(source_suffix, '.html')
absolute_from_path = Path(app.builder.outdir) / from_html_file
# Create the redirection.
absolute_from_path.parent.mkdir(parents=True, exist_ok=True)
absolute_from_path.write_text(TEMPLATE % to_html_file)
def setup(app):
app.add_config_value('redirects_file', 'redirects', 'env')
app.add_config_value('redirects_dir', 'redirects', 'env')
app.connect('builder-inited', generate_redirects)
return {