blob: f658e8d07700c6d85a63dd3bbbb7db5978a9beba [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2021 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
#### CATEGORY=Documentation
### Helper tool to batch move docs from one location to another
## Usage: fx move-docs [--dry-run] INPUT_FILE
## cat INPUT_FILE | fx move-docs [--dry-run]
## echo "docs/orig/file.md doc/new_location/_toc.yaml" | fx move-docs [--dry-run]
##
## INPUT_FILE is a file with two columns, where the first column
## is the original location of the doc to be moved, and the second
## line is the destination _toc.yaml file.
##
## This tool will locate the original and destination _toc.yaml files,
## update them and update any reference to the original file.
##
## It will also create a proper entry in the _redirects.yaml file.
##
import argparse
import os
import sys
import shutil
import re
import fnmatch
import pathlib
TOC_H="""# Copyright 2021 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Please, read https://fuchsia.dev/fuchsia-src/contribute/docs/documentation-navigation-toc
# before making changes to this file, and add a member of the fuchsia.dev
# team as reviewer.
toc: """
def process_file(orig, dest_yaml):
print("***** Processing %s:" % orig)
orig_yaml=os.path.join(os.path.dirname(orig), "_toc.yaml")
dest=os.path.join(os.path.dirname(dest_yaml), os.path.basename(orig))
if not os.path.basename(dest_yaml) == '_toc.yaml':
print("Destination TOC has to be named _toc.yaml: %s" % dest_yaml)
return False
if not os.path.exists(orig):
print("Original file does not exist: %s" % orig)
return False
if not os.path.exists(orig_yaml):
print("_toc.yaml for the original file does not exist: %s" % orig_yaml)
return False
if os.path.exists(dest):
print("Destination file exists: %s" % dest)
return False
if not os.path.exists(dest_yaml):
if dry_run:
print("...dryrun... Creating destination _toc.yaml: %s" % dest_yaml)
else:
pathlib.Path(os.path.dirname(dest_yaml)).mkdir(parents=True, exist_ok=True)
with open(dest_yaml, "w") as f:
f.write(TOC_H)
# We can't use yaml library because it throws away comments and styles. Since
# we just want to do basic manipulation on relatively well-behaved yaml
# files, we will hack it.
with open(orig_yaml) as f:
orig_lines = f.readlines()
new_lines = []
entry=[]
target_entry=None
started_toc=False
is_target=False
for line in orig_lines:
if not started_toc:
new_lines.append(line)
if re.search(r'^\s*toc:\s*$', line):
started_toc=True
continue
if re.search(r'^\s*- ', line):
if len(entry):
if is_target:
target_entry = entry
else:
new_lines.extend(entry)
entry = []
is_target = False
elif re.search(r'\s*[\s-]\s*path:\s*"?' + re.escape("/" + orig), line):
is_target = True
entry.append(line)
if len(entry):
if is_target:
target_entry = entry
else:
new_lines.extend(entry)
if not target_entry:
print("ERROR: could not find the original TOC entry in %s" % orig_yaml)
return False
if dry_run:
print("...dryrun... Removing entry from %s:" % orig_yaml)
print(''.join(target_entry))
else:
with open(orig_yaml, 'w') as f:
print(''.join(new_lines), file=f)
# Add the entry to the destination _toc.yaml file
new_target_entry = [re.sub(re.escape(orig), dest, line) for line in target_entry]
if dry_run:
print("...dryrun... Adding entry to the end of %s:" % dest_yaml)
print(''.join(new_target_entry))
else:
with open(dest_yaml, 'a') as f:
print('\n', file=f)
print(''.join(new_target_entry), file=f)
# Move the original file to the destination
if dry_run:
print("...dryrun... Moving file %s to %s" % (orig, dest))
else:
shutil.move(orig, dest)
# Replace usage of the original file in all *.md docs
docs_dir=os.path.join(os.environ['FUCHSIA_DIR'], 'docs')
find_replace(docs_dir, orig, dest, '*.md')
# Add an entry to _redirects.yaml
redirect=['- from: /%s' % orig, ' to: /%s' % dest]
redirect_file=os.path.join(docs_dir, '_redirects.yaml')
if dry_run:
print('...dryrun... Adding entry to docs/_redirects.yaml:')
print('\n'.join(redirect))
print('\n')
else:
with open(redirect_file, 'a' if os.path.exists(redirect_file) else 'w') as f:
print('\n', file=f)
print('\n'.join(redirect), file=f)
return True
def find_replace(directory, find, replace, filePattern):
for path, dirs, files in os.walk(os.path.abspath(directory)):
for filename in fnmatch.filter(files, filePattern):
filepath = os.path.join(path, filename)
with open(filepath) as f:
s = f.read()
new_s = s.replace(find, replace)
if s != new_s:
if dry_run:
print("### Changing references in %s" %
os.path.relpath(filepath, directory))
else:
with open(filepath, "w") as f:
f.write(new_s)
def is_file_toc(filename, toc):
if filename[0] != '/':
filename='/' + filename
return toc['path'] == filename
def main():
parser = argparse.ArgumentParser(
description=u"Move docs in batch")
parser.add_argument(
"input_file",
nargs=u"?",
default=u"",
help="Filename with one entry per line, each entry being space-separated strings for the original file and the destination _toc.yaml")
parser.add_argument(
"--dry-run",
action="store_const",
default=False,
const=True,
help="Does not modify any file",
)
args = parser.parse_args()
global dry_run
dry_run = args.dry_run
if args.input_file:
input_file = open(args.input_file)
else:
input_file = sys.stdin
for line in input_file.readlines():
orig, dest_yaml = line.split()
orig = orig[1:] if orig.startswith('/docs') else orig
dest_yaml = dest_yaml[1:] if dest_yaml.startswith('/docs') else dest_yaml
process_file(orig, dest_yaml)
if input_file is not sys.stdin:
input_file.close()
sys.exit(0)
if __name__ == u"__main__":
sys.exit(main())