|
| 1 | +#!/usr/bin/env python |
| 2 | + |
| 3 | +import re |
| 4 | +import os |
| 5 | +import requests |
| 6 | +import urllib |
| 7 | +import json |
| 8 | +from requests.auth import HTTPBasicAuth |
| 9 | +from subprocess import check_output |
| 10 | + |
| 11 | +# NOTE Does not follow docs.json, instead relies heavily on |
| 12 | +# remembering successful urls |
| 13 | + |
| 14 | +MBED_BOT_USER = "mbed-pr-bot" |
| 15 | +MBED_BOT_EMAIL = MBED_BOT_USER + "@arm.com" |
| 16 | +MBED_BOT_TOKEN = "9038ab8321dbe1581185c1dc0b33572acd215426" |
| 17 | +REPO_SLUG = "ARMmbed/Handbook" |
| 18 | +REPO_API_URL = "https://api.github.com/repos/" + REPO_SLUG |
| 19 | +REPO_PUSH_URL = ("https://" + MBED_BOT_USER + ":" + MBED_BOT_TOKEN + |
| 20 | + "@github.com/" + MBED_BOT_USER + "/" + REPO_SLUG.split('/')[1]) |
| 21 | + |
| 22 | +REDIRECT_NEW_TEMPLATE = ( |
| 23 | + "<span class=\"warnings\">**Out of date**: " |
| 24 | + "This is not the most recent version of this page. " |
| 25 | + "Please see [the most recent version]({url})</span>\n" |
| 26 | +) |
| 27 | +REDIRECT_OLD_TEMPLATE = REDIRECT_NEW_TEMPLATE |
| 28 | +DOCS_URL_PREFIX = "https://os.mbed.com/docs/v{version}" |
| 29 | +DOCS_PATH_PREFIX = "docs" |
| 30 | +DOCS_BRANCH = 'mbed-pr-bot-redirect-{version}' |
| 31 | + |
| 32 | +COMMIT_MESSAGE = "Updated version redirects from {version} to {latest}" |
| 33 | +PR_BODY = ( |
| 34 | + "This is an automated PR to update outdated doc pages with note pointing " |
| 35 | + "to the most recent version of that page. Feel free to reject or modify " |
| 36 | + "this PR as needed.\n\n" |
| 37 | + "Updated:\n```\n{redirects}\n```\n" |
| 38 | +) |
| 39 | + |
| 40 | +def edit(path, latest, seen): |
| 41 | + assert path.endswith('.md') |
| 42 | + url = None |
| 43 | + exists = False |
| 44 | + |
| 45 | + with open(os.path.join(DOCS_PATH_PREFIX, path), 'r+') as file: |
| 46 | + data = file.read() |
| 47 | + pattern = REDIRECT_NEW_TEMPLATE.split('{url}') |
| 48 | + if data.startswith(pattern[0]): |
| 49 | + begin = len(pattern[0]) |
| 50 | + end = data[begin:].find(pattern[1]) + begin |
| 51 | + if end > begin: |
| 52 | + url = data[begin:end] |
| 53 | + exists = True |
| 54 | + |
| 55 | + try: |
| 56 | + if url: |
| 57 | + requests.get(url).raise_for_status() |
| 58 | + except requests.exceptions.RequestException: |
| 59 | + url = None |
| 60 | + |
| 61 | + if not url: |
| 62 | + if path in seen: |
| 63 | + url = seen[path] |
| 64 | + |
| 65 | + if not url: |
| 66 | + print "Can't find \"%s\"" % path |
| 67 | + url = raw_input("What url should I use? ").strip() |
| 68 | + if not url: |
| 69 | + url = None |
| 70 | + |
| 71 | + try: |
| 72 | + if url: |
| 73 | + requests.get(url).raise_for_status() |
| 74 | + except requests.exceptions.RequestException: |
| 75 | + url = None |
| 76 | + |
| 77 | + if url: |
| 78 | + seen[path] = url |
| 79 | + |
| 80 | + if url and not exists: |
| 81 | + print path, '->', url |
| 82 | + file.seek(0) |
| 83 | + file.write(REDIRECT_NEW_TEMPLATE.format(url=url)) |
| 84 | + file.write(data) |
| 85 | + return path, url |
| 86 | + |
| 87 | +def main(): |
| 88 | + # Get root |
| 89 | + root = check_output(['git', 'rev-parse', '--show-toplevel']).strip() |
| 90 | + os.chdir(root) |
| 91 | + print 'Using repo at:', root |
| 92 | + |
| 93 | + # Get branch info |
| 94 | + orig_branch = check_output(['git', 'name-rev', '--name-only', 'HEAD']).strip() |
| 95 | + branches = requests.get(REPO_API_URL+"/branches").json() |
| 96 | + |
| 97 | + # Ask for versions |
| 98 | + latest = raw_input('What is the latest version? ').lstrip('v') |
| 99 | + print 'Using latest:', latest |
| 100 | + |
| 101 | + versions = raw_input('What versions do you want to update? ') |
| 102 | + versions = [ |
| 103 | + version.lstrip('v') for version in re.split('[, ]*', versions) |
| 104 | + if version] |
| 105 | + if not versions: |
| 106 | + versions = [ |
| 107 | + branch['name'] for branch in branches |
| 108 | + if re.match(r'^\d*\.\d*$', branch['name'])] |
| 109 | + |
| 110 | + if latest in versions: |
| 111 | + versions.remove(latest) |
| 112 | + |
| 113 | + branches = { |
| 114 | + branch['name']: branch for branch in branches |
| 115 | + if branch['name'] in versions} |
| 116 | + |
| 117 | + print 'Using versions:' |
| 118 | + print ', '.join(branches[version]['name'] for version in versions) |
| 119 | + |
| 120 | + seen = {} |
| 121 | + redirects = {} |
| 122 | + |
| 123 | + # Edit stuff |
| 124 | + for version in versions: |
| 125 | + print 'Checking out %s...' % version |
| 126 | + check_output(['git', 'checkout', '-q', |
| 127 | + branches[version]['commit']['sha']]) |
| 128 | + print 'Editing %s...' % version |
| 129 | + redirects[version] = [] |
| 130 | + |
| 131 | + for dir, dirs, files in os.walk(DOCS_PATH_PREFIX): |
| 132 | + for file in files: |
| 133 | + if not file.endswith('.md'): |
| 134 | + continue |
| 135 | + |
| 136 | + path = os.path.join( |
| 137 | + os.path.relpath(dir, DOCS_PATH_PREFIX).lstrip('./'), |
| 138 | + file) |
| 139 | + |
| 140 | + redirect = edit(path, latest, seen) |
| 141 | + if redirect: |
| 142 | + redirects[version].append(redirect) |
| 143 | + |
| 144 | + for dir in dirs: |
| 145 | + if dir.startswith('.'): |
| 146 | + dirs.remove(dir) |
| 147 | + |
| 148 | + if redirects[version]: |
| 149 | + print 'Committing %s...' % version |
| 150 | + check_output(['git', 'checkout', '-B', |
| 151 | + DOCS_BRANCH.format(version=version)]) |
| 152 | + check_output(['git', |
| 153 | + '-c', 'user.name=' + MBED_BOT_USER, |
| 154 | + '-c', 'user.email=' + MBED_BOT_EMAIL, |
| 155 | + 'commit', '-a', |
| 156 | + '-m', COMMIT_MESSAGE.format(version=version, latest=latest)]) |
| 157 | + |
| 158 | + # Push/pr stuff |
| 159 | + print 'Done redirecting version!' |
| 160 | + create_prs = raw_input('Create prs? ') |
| 161 | + if create_prs in ['y', 'yes']: |
| 162 | + branch_names = [DOCS_BRANCH.format(version=version) |
| 163 | + for version in versions |
| 164 | + if redirects[version]] |
| 165 | + check_output(['git', 'push', '-f', REPO_PUSH_URL] + branch_names) |
| 166 | + |
| 167 | + for version in versions: |
| 168 | + if not redirects[version]: |
| 169 | + continue |
| 170 | + |
| 171 | + res = requests.post(REPO_API_URL+"/pulls", |
| 172 | + auth=(MBED_BOT_USER, MBED_BOT_TOKEN), |
| 173 | + json={ |
| 174 | + "title": COMMIT_MESSAGE.format(version=version, latest=latest), |
| 175 | + "body": PR_BODY.format(version=version, latest=latest, |
| 176 | + redirects='\n'.join('%s -> %s' % r for r in redirects[version])), |
| 177 | + "head": "%s:%s" % (MBED_BOT_USER, DOCS_BRANCH.format(version=version)), |
| 178 | + "base": version, |
| 179 | + }) |
| 180 | + res.raise_for_status() |
| 181 | + print 'Created #%d' % res.json()['number'] |
| 182 | + |
| 183 | + check_output(['git', 'checkout', '-q', orig_branch]) |
| 184 | + print 'Done!' |
| 185 | + |
| 186 | +if __name__ == "__main__": |
| 187 | + import sys |
| 188 | + main(*sys.argv[1:]) |
0 commit comments