Parse command-line arguments

def parse_args():
    """Parse command-line arguments"""
    prog = 'github-tag-release'

    parser = argparse.ArgumentParser(
        prog=prog,
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=textwrap.dedent("""
            Tag git repositories, in a GitHub org, that correspond to the
            products in a published eups distrib tag.

            Examples:

                # eups tag is derived from git tag
                {prog} \\
                    --dry-run \\
                    --debug \\
                    --limit 10 \\
                    --org lsst \\
                    --allow-team 'Data Management' \\
                    --allow-team 'DM Externals' \\
                    'w.2018.18' 'b3595'

                # explicit eups tag and git tag
                {prog} \\
                    --dry-run \\
                    --debug \\
                    --limit 10 \\
                    --org lsst \\
                    --allow-team 'Data Management' \\
                    --allow-team 'DM Externals' \\
                    --external-team 'DM Externals' \\
                    --eups-tag v11_0_rc2 \\
                    11.0.rc2 b1679

                # verify a past eups tag + git tag release
                {prog} \\
                    --verify \\
                    --debug \\
                    --limit 10 \\
                    --org 'lsst' \\
                    --allow-team 'Data Management' \\
                    --allow-team 'DM Externals' \\
                    --external-team 'DM Externals' \\
                    --deny-team 'DM Auxilliaries' \\
                    --email 'sqre-admin@lists.lsst.org' \\
                    --user 'sqreadmin' \\
                    --token "$GITHUB_TOKEN" \\
                    --ignore-git-tagger \\
                    --ignore-git-message \\
                    --manifest 'b3595' \\
                    'w.2018.18'

                # tag a git release from a manifest *without* a pre-existing
                # eups tag.
                {prog} \\
                    --dry-run \\
                    --debug \\
                    --limit 10 \\
                    --org 'lsst' \\
                    --allow-team 'Data Management' \\
                    --allow-team 'DM Externals' \\
                    --external-team 'DM Externals' \\
                    --deny-team 'DM Auxilliaries' \\
                    --email 'sqre-admin@lists.lsst.org' \\
                    --user 'sqreadmin' \\
                    --token "$GITHUB_TOKEN" \\
                    --ignore-git-tagger \\
                    --ignore-git-message \\
                    --manifest 'b3595' \\
                    --manifest-only \\
                    'w.2018.18'

            Note that the access token must have access to these oauth scopes:
                * read:org
                * repo

            The token generated by `github-auth --user` should have sufficient
            permissions.
        """).format(prog=prog),
        epilog='Part of codekit: https://github.com/lsst-sqre/sqre-codekit'
    )

    parser.add_argument(
        '--manifest',
        required=True,
        help='Name of versiondb manifest for git repo sha resolution'
             ' AKA bNNNN')
    parser.add_argument(
        '--org',
        required=True,
        help='Github organization')
    parser.add_argument(
        '--allow-team',
        action='append',
        required=True,
        help='git repos to be tagged MUST be a member of ONE or more of'
             ' these teams (can specify several times)')
    parser.add_argument(
        '--external-team',
        action='append',
        help='git repos in this team MUST not have tags that start with a'
             ' number. Any requested tag that violates this policy will be'
             ' prefixed with \'v\' (can specify several times)')
    parser.add_argument(
        '--deny-team',
        action='append',
        help='git repos to be tagged MUST NOT be a member of ANY of'
             ' these teams (can specify several times)')
    parser.add_argument(
        '--user',
        help='Name of person making the tag - defaults to gitconfig value')
    parser.add_argument(
        '--email',
        help='Email address of tagger - defaults to gitconfig value')
    parser.add_argument(
        '--token-path',
        default='~/.sq_github_token_delete',
        help='Use a token (made with github-auth) in a non-standard location')
    parser.add_argument(
        '--token',
        default=None,
        help='Literal github personal access token string')
    parser.add_argument(
        '--versiondb-base-url',
        default=os.getenv('LSST_VERSIONDB_BASE_URL'),
        help='Override the default versiondb base url')
    parser.add_argument(
        '--eupstag-base-url',
        default=os.getenv('LSST_EUPSTAG_BASE_URL'),
        help='Override the default eupstag base url')
    parser.add_argument(
        '--force-tag',
        action='store_true',
        help='Force moving pre-existing annotated git tags.')
    parser.add_argument(
        '--ignore-manifest-versions',
        action='store_true',
        help='Ignore manifest version strings'
             ' when cross referencing eups tag and manifest data.')
    parser.add_argument(
        '--ignore-git-message',
        action='store_true',
        help='Ignore git tag message when verifying an existing tag.')
    parser.add_argument(
        '--ignore-git-tagger',
        action='store_true',
        help='Ignore git tag "tagger"/author when verifying an existing tag.')
    parser.add_argument(
        '--limit',
        default=None,
        type=int,
        help='Maximum number of products/repos to tags. (useful for testing)')
    parser.add_argument(
        '--fail-fast',
        action='store_true',
        help='Fail immediately on github API error.')
    parser.add_argument(
        '--no-fail-fast',
        action='store_const',
        const=False,
        dest='fail_fast',
        help='DO NOT Fail immediately on github API error(s). (default)')
    parser.add_argument(
        '-d', '--debug',
        action='count',
        default=codetools.debug_lvl_from_env(),
        help='Debug mode (can specify several times)')
    parser.add_argument('-v', '--version', action=codetools.ScmVersionAction)
    parser.add_argument('tag')

    manifest_group = parser.add_mutually_exclusive_group()
    manifest_group.add_argument(
        '--eups-tag',
        help='(mutually exclusive with --manifest-only)')
    manifest_group.add_argument(
        '--manifest-only',
        action='store_true',
        help='Do not cross reference a published eups tag with the manifest'
             ' -- use only the metadata from the manifest to determine'
             ' git tag location.'
             ' This allows a git tag to be created without a prior eups tag.'
             ' (mutually exclusive with --eups-tag)')

    dryrun_group = parser.add_mutually_exclusive_group()
    dryrun_group.add_argument(
        '--dry-run',
        action='store_true',
        help='Do not create/update tag(s) or modify any state.'
             ' (mutually exclusive with --verify)')
    dryrun_group.add_argument(
        '--verify',
        action='store_true',
        help='Verify that all git tags for a release are present and correct.'
             ' will not create/update tag(s) or modify any state.'
             ' (mutually exclusive with --dry-run)')

    return parser.parse_args()