You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by br...@apache.org on 2019/10/14 21:00:14 UTC

[allura] 09/09: [#8336] Remove one-time and very old scripts

This is an automated email from the ASF dual-hosted git repository.

brondsem pushed a commit to branch db/8336
in repository https://gitbox.apache.org/repos/asf/allura.git

commit 5bd61b7981cca530cc300820af5efc5608589e14
Author: Dave Brondsema <da...@brondsema.net>
AuthorDate: Mon Oct 14 15:43:33 2019 -0400

    [#8336] Remove one-time and very old scripts
---
 Allura/allura/scripts/create_deleted_comments.py | 110 -------------
 Allura/allura/scripts/trim_emails.py             |  54 ------
 Allura/allura/scripts/update_checkout_url.py     |  53 ------
 scripts/create-moved-tickets.py                  |  72 --------
 scripts/emsignia-MOBILITY-red.png                | Bin 648 -> 0 bytes
 scripts/emsignia-SOLUTIONS-blue.gif              | Bin 2033 -> 0 bytes
 scripts/fix-wiki-page-names.py                   |  92 -----------
 scripts/git-mr                                   | 113 -------------
 scripts/test-branches-against-tickets.py         | 200 -----------------------
 9 files changed, 694 deletions(-)

diff --git a/Allura/allura/scripts/create_deleted_comments.py b/Allura/allura/scripts/create_deleted_comments.py
deleted file mode 100644
index 7b3c455..0000000
--- a/Allura/allura/scripts/create_deleted_comments.py
+++ /dev/null
@@ -1,110 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-import sys
-import argparse
-import logging
-
-from ming.odm import session
-from tg import tmpl_context as c
-
-from allura.scripts import ScriptTask
-from allura import model as M
-from allura.lib.utils import chunked_find
-from forgediscussion.model import ForumPost
-
-
-log = logging.getLogger(__name__)
-
-
-class CreateDeletedComments(ScriptTask):
-
-    @classmethod
-    def execute(cls, options):
-        models = [M.Post, ForumPost]
-        app_config_id = cls.get_tool_id(options.tool)
-        # Find all posts that have parent_id, but does not have actual parent
-        # and create fake parent for them
-        for model in models:
-            q = {'parent_id': {'$ne': None},
-                 'app_config_id': app_config_id}
-            for chunk in chunked_find(model, q):
-                for post in chunk:
-                    if not post.parent:
-                        log.info('Creating deleted parent for %s %s',
-                                 model.__mongometa__.name, post._id)
-                        c.project = post.app_config.project
-                        slug = post.slug.rsplit('/', 1)[0]
-                        full_slug = post.full_slug.rsplit('/', 1)[0]
-                        author = c.project.admins()[0]
-                        deleted_post = model(
-                            _id=post.parent_id,
-                            deleted=True,
-                            text="Automatically created in place of deleted post",
-                            app_id=post.app_id,
-                            app_config_id=post.app_config_id,
-                            discussion_id=post.discussion_id,
-                            thread_id=post.thread_id,
-                            author_id=author._id,
-                            slug=slug,
-                            full_slug=full_slug,
-                        )
-                        if options.dry_run:
-                            session(deleted_post).expunge(deleted_post)
-                        else:
-                            session(deleted_post).flush(deleted_post)
-
-    @classmethod
-    def get_tool_id(cls, tool):
-        _n, _p, _mount = tool.split('/')
-        n = M.Neighborhood.query.get(url_prefix='/{}/'.format(_n))
-        if not n:
-            log.error('Can not find neighborhood %s', _n)
-            sys.exit(1)
-        p = M.Project.query.get(neighborhood_id=n._id, shortname=_p)
-        if not p:
-            log.error('Can not find project %s', _p)
-            sys.exit(1)
-        t = p.app_instance(_mount)
-        if not t:
-            log.error('Can not find tool with mount point %s', _mount)
-            sys.exit(1)
-        return t.config._id
-
-    @classmethod
-    def parser(cls):
-        parser = argparse.ArgumentParser(
-            description='Create comments marked as deleted in place of '
-                        'actually deleted parent comments (ticket:#1731)'
-        )
-        parser.add_argument(
-            '--dry-run',
-            action='store_true',
-            dest='dry_run',
-            default=False,
-            help='Show comments that will be created, but do not actually '
-                 'create anything',
-        )
-        parser.add_argument(
-            '-t', '--tool',
-            required=True,
-            help='Create comments only in specified tool, e.g. p/test/wiki')
-        return parser
-
-
-if __name__ == '__main__':
-    CreateDeletedComments.main()
diff --git a/Allura/allura/scripts/trim_emails.py b/Allura/allura/scripts/trim_emails.py
deleted file mode 100644
index f847224..0000000
--- a/Allura/allura/scripts/trim_emails.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-import logging
-
-from ming.orm import session
-
-from allura.scripts import ScriptTask
-from allura import model as M
-from allura.lib.utils import chunked_find
-
-
-log = logging.getLogger(__name__)
-
-
-class TrimEmails(ScriptTask):
-
-    @classmethod
-    def execute(cls, options):
-        for chunk in chunked_find(M.User, {}):
-            for u in chunk:
-                log.info('Trimming emails for user %s', u.username)
-                new_addresses = [M.EmailAddress.canonical(addr) for addr in u.email_addresses]
-                u.email_addresses = new_addresses
-                if u.preferences.email_address is not None:
-                    u.preferences.email_address = M.EmailAddress.canonical(
-                        u.preferences.email_address)
-                session(u).flush(u)
-        for chunk in chunked_find(M.EmailAddress, {}):
-            for a in chunk:
-                log.info('Trimming email address entry %s', a.email)
-                a.email = M.EmailAddress.canonical(a.email)
-                session(a).flush(a)
-        M.main_orm_session.flush()
-        M.main_orm_session.clear()
-        log.info('Finished trimming emails')
-
-
-if __name__ == '__main__':
-    TrimEmails.main()
diff --git a/Allura/allura/scripts/update_checkout_url.py b/Allura/allura/scripts/update_checkout_url.py
deleted file mode 100644
index a1fc982..0000000
--- a/Allura/allura/scripts/update_checkout_url.py
+++ /dev/null
@@ -1,53 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-"""
-Find repos with blank checkout url and top-level "trunk" dir,
-and point checkout_url at trunk.
-"""
-
-import logging
-
-from ming.orm import ThreadLocalORMSession
-
-from allura import model as M
-from allura.lib import utils
-from allura.scripts import ScriptTask
-
-from forgesvn.model.svn import Repository, svn_path_exists
-
-log = logging.getLogger(__name__)
-
-
-class UpdateCheckoutUrl(ScriptTask):
-
-    @classmethod
-    def execute(cls, options):
-        query = {'tool_name': {'$regex': '^svn$', '$options': 'i'},
-                 'options.checkout_url': ''}
-        for chunk in utils.chunked_find(M.AppConfig, query):
-            for config in chunk:
-                repo = Repository.query.get(app_config_id=config._id)
-                trunk_path = "file://{0}{1}/trunk".format(repo.fs_path,
-                                                          repo.name)
-                if svn_path_exists(trunk_path):
-                    config.options['checkout_url'] = "trunk"
-                    log.info("Update checkout_url for: %s", trunk_path)
-            ThreadLocalORMSession.flush_all()
-
-if __name__ == '__main__':
-    UpdateCheckoutUrl.main()
diff --git a/scripts/create-moved-tickets.py b/scripts/create-moved-tickets.py
deleted file mode 100644
index 04ce906..0000000
--- a/scripts/create-moved-tickets.py
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env python
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-
-'''
-This is for making redirects for tickets that we move from SourceForge
-to Apache, but could be generalized pretty easily to work for making
-any type of redirect (change SF/Apache specifics to commandline arguments)
-'''
-
-import argparse
-import pymongo
-
-
-def get_opts():
-    parser = argparse.ArgumentParser(
-        description='Create MovedTicket records for SourceForge tickets that will be moved to Apache')
-    parser.add_argument('-n', help='Neighborhood', default='p')
-    parser.add_argument('-p', help='Project', default='allura')
-    parser.add_argument('-t', help='Tool mount point', default='tickets')
-    parser.add_argument('-f', '--file', help='Path to file with ticket numbers')
-    parser.add_argument('--dry-run', help='Run in test mode', action='store_true')
-    parser.add_argument('-m', '--mongo', help='Mongo connection string', default='mongodb://localhost:27017/')
-    parser.add_argument('--db', help='Database name', default='allura')
-    opts = parser.parse_args()
-    return opts
-
-opts = get_opts()
-ticket_nums = open(opts.file, 'r').read().split()
-ticket_nums = [int(num.strip()) for num in ticket_nums]
-
-db = pymongo.MongoClient(opts.mongo)
-main_db = db[opts.db]
-project_data = db['project-data']
-
-nbhd = main_db.neighborhood.find_one({'url_prefix': '/%s/' % opts.n})
-project = main_db.project.find_one({'neighborhood_id': nbhd['_id'], 'shortname': opts.p})
-tool = project_data.config.find_one({'project_id': project['_id'], 'options.mount_point': opts.t})
-
-print "Tool id: %s" % tool['_id']
-print 'Setting app_config_id to: %s for tickets: %s' % ('moved-to-apache', ticket_nums)
-
-if not opts.dry_run:
-    project_data.ticket.update({
-        'app_config_id': tool['_id'],
-        'ticket_num': {'$in': ticket_nums}
-    }, {'$set': {'app_config_id': 'moved-to-apache'}}, multi=True)
-
-print 'Creating MovingTickets for tickets: %s' % ticket_nums
-
-if not opts.dry_run:
-    for num in ticket_nums:
-        project_data.moved_ticket.insert({
-            'app_config_id': tool['_id'],
-            'ticket_num': num,
-            'moved_to_url': 'https://forge-allura.apache.org/p/allura/tickets/%s' % num,
-        })
diff --git a/scripts/emsignia-MOBILITY-red.png b/scripts/emsignia-MOBILITY-red.png
deleted file mode 100644
index f1243f3..0000000
Binary files a/scripts/emsignia-MOBILITY-red.png and /dev/null differ
diff --git a/scripts/emsignia-SOLUTIONS-blue.gif b/scripts/emsignia-SOLUTIONS-blue.gif
deleted file mode 100644
index 0bf5700..0000000
Binary files a/scripts/emsignia-SOLUTIONS-blue.gif and /dev/null differ
diff --git a/scripts/fix-wiki-page-names.py b/scripts/fix-wiki-page-names.py
deleted file mode 100644
index b53e708..0000000
--- a/scripts/fix-wiki-page-names.py
+++ /dev/null
@@ -1,92 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-"""Rename page/title to page-title"""
-
-import sys
-import logging
-
-from ming.orm import session
-
-from allura import model as M
-from allura.lib import helpers as h
-from allura.lib.utils import chunked_find
-from forgewiki.model import Page
-
-
-log = logging.getLogger(__name__)
-
-
-def error(msg):
-    log.error(msg)
-    sys.exit(1)
-
-
-def main(opts):
-    if opts.project and not opts.nbhd:
-        error('Specify neighborhood')
-    p_query = {}
-    if opts.nbhd:
-        nbhd = M.Neighborhood.query.get(url_prefix=opts.nbhd)
-        if not nbhd:
-            error("Can't find such neighborhood")
-        p_query['neighborhood_id'] = nbhd._id
-        if opts.project:
-            p_query['shortname'] = opts.project
-
-        projects = M.Project.query.find(p_query).all()
-        if not projects:
-            error('No project matches given parameters')
-
-        app_config_ids = []
-        for p in projects:
-            for ac in p.app_configs:
-                if ac.tool_name.lower() == 'wiki':
-                    app_config_ids.append(ac._id)
-
-        if not app_config_ids:
-            error('No wikis in given projects')
-        query = {'app_config_id': {'$in': app_config_ids}}
-    else:
-        query = {}
-
-    M.artifact_orm_session._get().skip_last_updated = True
-    try:
-        for chunk in chunked_find(Page, query):
-            for page in chunk:
-                if '/' in page.title:
-                    log.info('Found {} in {}'.format(page.title, page.app_config.url()))
-                    page.title = page.title.replace('/', '-')
-                    with h.push_context(page.app_config.project._id, app_config_id=page.app_config_id):
-                        session(page).flush(page)
-    finally:
-        M.artifact_orm_session._get().skip_last_updated = False
-
-
-def parse_options():
-    import argparse
-    parser = argparse.ArgumentParser(description=__doc__,
-                                     formatter_class=argparse.RawDescriptionHelpFormatter)
-    parser.add_argument('-n', '--nbhd', default=None, help='Neighborhood url_prefix. E.g. /p/. '
-                        'Default is all neighborhoods.')
-    parser.add_argument('-p', '--project', default=None, help='Project shortname. '
-                        'Default is all projects in given neighborhood.')
-    return parser.parse_args()
-
-
-if __name__ == '__main__':
-    main(parse_options())
diff --git a/scripts/git-mr b/scripts/git-mr
deleted file mode 100755
index 3e4916e..0000000
--- a/scripts/git-mr
+++ /dev/null
@@ -1,113 +0,0 @@
-#!/usr/bin/env python
-
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-import re
-import shlex
-import subprocess
-import sys
-try:
-    import argparse
-except ImportError:
-    print('requires package argparse or else Python >= 2.7')
-    sys.exit()
-
-try:
-    from pygments.console import colorize
-except ImportError:
-    def colorize(color, message):
-        return message
-
-usage = "git mr [-a|-r] [--grep PATTERN] [upstream]"
-
-
-def main():
-    parser = argparse.ArgumentParser(prog='git mr', usage=usage)
-    parser.add_argument('-r', action='store_true',
-                        dest='examine_remote_branches', default=False,
-                        help='examine remote branches')
-    parser.add_argument('-a', action='store_true',
-                        dest='examine_all_branches', default=False,
-                        help='examine all branches')
-    parser.add_argument('--grep', nargs=1, metavar='PATTERN', default=None,
-                        help='only examine branches matching PATTERN')
-    parser.add_argument('upstream', nargs='?', default='HEAD',
-                        help='the branch to which everything else is compared, defaults to HEAD')
-    args = parser.parse_args()
-
-    if args.examine_all_branches:
-        merged_branches = git('branch', '-a', '--merged', args.upstream)
-        unmerged_branches = git('branch', '-a', '--no-merged', args.upstream)
-    elif args.examine_remote_branches:
-        merged_branches = git('branch', '-r', '--merged', args.upstream)
-        unmerged_branches = git('branch', '-r', '--no-merged', args.upstream)
-    else:
-        merged_branches = git('branch', '--merged', args.upstream)
-        unmerged_branches = git('branch', '--no-merged', args.upstream)
-
-    merged_branches = [line[2:] for line in merged_branches]
-    unmerged_branches = [line[2:] for line in unmerged_branches]
-    really_unmerged_branches = []
-
-    if args.grep:
-        filter = re.compile(args.grep[0])
-        merged_branches = [b for b in merged_branches if filter.search(b)]
-        unmerged_branches = [b for b in unmerged_branches if filter.search(b)]
-
-    if merged_branches:
-        print('Branches contained by %s:' % args.upstream)
-        for branch in merged_branches:
-            print('  ' + colorize('green', branch))
-
-    if unmerged_branches:
-        header_printed = False
-        for branch in unmerged_branches:
-            commits = ''.join(
-                git('cherry', args.upstream, branch, strip_eol=False))
-            if commits.find('+') == -1:
-                if not header_printed:
-                    print('Branches probably merged to %s:' % args.upstream)
-                    header_printed = True
-                print('  ' + colorize('green', branch))
-            else:
-                really_unmerged_branches += [branch]
-
-        if really_unmerged_branches:
-            print('Branches probably not merged to %s:' % args.upstream)
-            for branch in really_unmerged_branches:
-                print('  ' + colorize('red', branch))
-
-
-def git(*args, **kw):
-    if len(args) == 1 and isinstance(args[0], basestring):
-        argv = shlex.split(args[0])
-    else:
-        argv = list(args)
-    if argv[0] != 'git':
-        argv.insert(0, 'git')
-    p = subprocess.Popen(argv, stdout=subprocess.PIPE,
-                         stderr=subprocess.STDOUT)
-    rc = p.wait()
-    output = p.stdout.readlines()
-    if kw.get('strip_eol', True):
-        output = [line.rstrip('\n') for line in output]
-    return output
-
-
-if __name__ == '__main__':
-    main()
diff --git a/scripts/test-branches-against-tickets.py b/scripts/test-branches-against-tickets.py
deleted file mode 100755
index a742f03..0000000
--- a/scripts/test-branches-against-tickets.py
+++ /dev/null
@@ -1,200 +0,0 @@
-#       Licensed to the Apache Software Foundation (ASF) under one
-#       or more contributor license agreements.  See the NOTICE file
-#       distributed with this work for additional information
-#       regarding copyright ownership.  The ASF licenses this file
-#       to you under the Apache License, Version 2.0 (the
-#       "License"); you may not use this file except in compliance
-#       with the License.  You may obtain a copy of the License at
-#
-#         http://www.apache.org/licenses/LICENSE-2.0
-#
-#       Unless required by applicable law or agreed to in writing,
-#       software distributed under the License is distributed on an
-#       "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-#       KIND, either express or implied.  See the License for the
-#       specific language governing permissions and limitations
-#       under the License.
-
-from ConfigParser import ConfigParser, NoOptionError
-import json
-import oauth2 as oauth
-import os
-import re
-import shlex
-import subprocess
-import sys
-import urlparse
-import webbrowser
-
-CP = ConfigParser()
-re_ticket_branch = re.compile('^\s*origin/.*/(\d+)$')
-
-
-def main():
-    target_dir = None
-    if len(sys.argv) > 1:
-        target_dir = sys.argv[1]
-    match_ticket_branches(target_dir)
-
-
-def match_ticket_branches(target_dir=None):
-    here = os.getcwd()
-    if target_dir:
-        os.chdir(target_dir)
-
-    git('remote prune origin')
-
-    # maps ticket numbers to the actual branch e.g., int(42) -> 'origin/rc/42'
-    branches_for_tickets = dict()
-    # maps ticket numbers to 'merged' or 'unmerged' according to the matching
-    # branch
-    ticket_nums = dict()
-    # maps ticket numbers to differences in (number of) commit messages
-    commit_diffs = dict()
-
-    merged_branches = [branch[2:]
-                       for branch in git('branch -r --merged dev') if re_ticket_branch.match(branch)]
-    unmerged_branches = [branch[2:]
-                         for branch in git('branch -r --no-merged dev') if re_ticket_branch.match(branch)]
-
-    for branch in merged_branches:
-        tn = int(re_ticket_branch.match(branch).group(1))
-        branches_for_tickets[tn] = branch
-        ticket_nums[tn] = 'merged'
-
-    for branch in unmerged_branches:
-        # we'll consider it merged if `git cherry` thinks it is
-        commits = ''.join(git('cherry', 'dev', branch, strip_eol=False))
-        tn = int(re_ticket_branch.match(branch).group(1))
-        branches_for_tickets[tn] = branch
-        if commits.find('+') == -1:
-            ticket_nums[tn] = 'merged'
-        else:
-            branch_commits = git('log --oneline dev..%s' % branch)
-            # count the number of commits on dev since this branch that contain
-            # the ticket #
-            merge_base = git('merge-base', 'dev', branch)[0]
-            matching_dev_commits = git(
-                'log --oneline --grep="\[#%s\]" %s..dev' % (tn, merge_base))
-
-            if len(matching_dev_commits) >= len(branch_commits):
-                ticket_nums[tn] = 'merged'
-            else:
-                ticket_nums[tn] = 'unmerged'
-                commit_diffs[tn] = '\t' + '\n\t'.join(['Branch has:'] + branch_commits +
-                                                      ['Dev has:'] + matching_dev_commits)
-
-    failure = False
-
-    CP.read(os.path.join(os.environ['HOME'], '.forgepushrc'))
-    oauth_client = make_oauth_client()
-
-    for tn in ticket_nums:
-        resp = oauth_client.request(
-            'http://sourceforge.net/rest/p/allura/tickets/%s/' % tn)
-        #assert resp[0]['status'] == '200', (resp, tn)
-        if resp[0]['status'] != '200':
-            continue
-        ticket = json.loads(resp[1])['ticket']
-        if ticket is None:
-            continue
-        is_closed = ticket['status'] in (
-            'closed', 'validation', 'wont-fix', 'invalid')
-        is_merged = ticket_nums[tn] == 'merged'
-
-        if is_closed != is_merged:
-            print(
-                '<http://sourceforge.net/p/allura/tickets/%s/> is status:"%s", but the branch "%s" is %s' %
-                (tn, ticket['status'], branches_for_tickets[tn], ticket_nums[tn]))
-            if tn in commit_diffs:
-                print(commit_diffs[tn])
-            failure = True
-
-    os.chdir(here)
-    if failure:
-        sys.exit(1)
-
-
-def make_oauth_client():
-    """
-    Build an oauth.Client with which callers can query Allura.
-    See format_changes for an example use.
-
-    Uses global CP, a ConfigParser
-
-    Re-usable - copy & pasted between Allura, sfpy, and sfx push scripts
-    """
-
-    # https://sourceforge.net/p/forge/documentation/API%20-%20Beta/
-    REQUEST_TOKEN_URL = 'http://sourceforge.net/rest/oauth/request_token'
-    AUTHORIZE_URL = 'https://sourceforge.net/rest/oauth/authorize'
-    ACCESS_TOKEN_URL = 'http://sourceforge.net/rest/oauth/access_token'
-    oauth_key = option('re', 'oauth_key',
-                       'Forge API OAuth Key (https://sourceforge.net/auth/oauth/): ')
-    oauth_secret = option('re', 'oauth_secret', 'Forge API Oauth Secret: ')
-    consumer = oauth.Consumer(oauth_key, oauth_secret)
-
-    try:
-        oauth_token = CP.get('re', 'oauth_token')
-        oauth_token_secret = CP.get('re', 'oauth_token_secret')
-    except NoOptionError:
-        client = oauth.Client(consumer)
-        resp, content = client.request(REQUEST_TOKEN_URL, 'GET')
-        assert resp['status'] == '200', resp
-
-        request_token = dict(urlparse.parse_qsl(content))
-        pin_url = "%s?oauth_token=%s" % (
-            AUTHORIZE_URL, request_token['oauth_token'])
-        if getattr(webbrowser.get(), 'name', '') == 'links':
-            # sandboxes
-            print("Go to %s" % pin_url)
-        else:
-            webbrowser.open(pin_url)
-        oauth_verifier = raw_input('What is the PIN? ')
-
-        token = oauth.Token(
-            request_token['oauth_token'], request_token['oauth_token_secret'])
-        token.set_verifier(oauth_verifier)
-        client = oauth.Client(consumer, token)
-        resp, content = client.request(ACCESS_TOKEN_URL, "GET")
-        access_token = dict(urlparse.parse_qsl(content))
-        oauth_token = access_token['oauth_token']
-        oauth_token_secret = access_token['oauth_token_secret']
-
-        CP.set('re', 'oauth_token', oauth_token)
-        CP.set('re', 'oauth_token_secret', oauth_token_secret)
-
-    access_token = oauth.Token(oauth_token, oauth_token_secret)
-    return oauth.Client(consumer, access_token)
-
-
-def git(*args, **kw):
-    if len(args) == 1 and isinstance(args[0], basestring):
-        argv = shlex.split(args[0])
-    else:
-        argv = list(args)
-    if argv[0] != 'git':
-        argv.insert(0, 'git')
-    p = subprocess.Popen(argv, stdout=subprocess.PIPE,
-                         stderr=subprocess.STDOUT)
-    p.wait()
-    output = p.stdout.readlines()
-    if kw.get('strip_eol', True):
-        output = [line.rstrip('\n') for line in output]
-    return output
-
-
-def option(section, key, prompt=None):
-    """ shared (copy/paste) between Allura & sfpy """
-    if not CP.has_section(section):
-        CP.add_section(section)
-    if CP.has_option(section, key):
-        value = CP.get(section, key)
-    else:
-        value = raw_input(prompt or ('%s: ' % key))
-        CP.set(section, key, value)
-    return value
-
-
-if __name__ == '__main__':
-    main()