Example #1
0
def git_merge(base: str, head: str, no_ff: bool = False):
    """ Merge *head* into *base*.

    Args:
        base (str):
            The base branch. *head* will be merged into this branch.
        head (str):
            The branch that will be merged into *base*.
        no_ff (bool):
            If set to **True** it will force git to create merge commit. If set
            to **False** (default) it will do a fast-forward merge if possible.
    """
    pretend = context.get('pretend', False)
    branch = git.current_branch(refresh=True)

    if branch.name != base and not pretend:
        git_checkout(base)

    args = []

    if no_ff:
        args.append('--no-ff')

    log.info("Merging <33>{}<32> into <33>{}<32>", head, base)
    shell.run('git merge {args} {branch}'.format(
        args=' '.join(args),
        branch=head,
    ))

    if branch.name != base and not pretend:
        git_checkout(branch.name)
Example #2
0
    def deploy(self, promote=False, quiet=False):
        # type: (bool, bool, bool) -> None
        """ Deploy the code to AppEngine.

        Args:
            promote (bool):
                Migrate the traffic to the deployed version.
            quiet (bool):
                Pass ``--quiet`` flag to gcloud command
        """
        args = [
            '--promote' if promote else '--no-promote',
            '--version {}'.format(self.app_version),
            '--project {}'.format(self.app_id),
        ]

        if quiet:
            args += ['--quiet']

        cmd = 'gcloud app deploy {args} {deployables}'.format(
            deployables=fs.wrap_paths(self.deployables), args=' '.join(args))

        if context.get('pretend', False):
            log.info("Would deploy version <35>{ver}<32> to <35>{app}".format(
                ver=self.app_version, app=self.app_id))
            shell.cprint('<90>{}', cmd)
        else:
            log.info("Deploying version <35>{ver}<32> to <35>{app}".format(
                ver=self.app_version,
                app=self.app_id,
            ))
            shell.run(cmd)
Example #3
0
def delete_remote():
    """ Delete the current branch on origin.

    This is an equivalent of ``git push origin :<branch>``. Easy way to quickly
    delete the remote branch without having to type in the branch name.
    """
    branch = git.current_branch().name
    shell.run('git push -u origin {}'.format(branch))
Example #4
0
def test_setting_capture_to_True_will_pipe_stdout_and_stderr(p_popen):
    shell.run('hello', capture=True)

    p_popen.assert_called_once_with(
        'hello',
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
Example #5
0
def git_pull(branch_name: str):
    """ Pull from remote branch.

    Args:
        branch_name (str):
            The remote branch to pull.
    """
    log.info("Pulling latest changes on <33>{}", branch_name)
    shell.run('git pull origin {}'.format(branch_name))
Example #6
0
def test_will_not_crash_if_communicated_returns_strings(p_popen):
    shell.run('hello', capture=True)

    p_popen.assert_called_once_with(
        'hello',
        shell=True,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
Example #7
0
def push():
    """ Push the current branch to origin.

    This is an equivalent of ``git push -u origin <branch>``. Mainly useful for
    the first push as afterwards ``git push`` is just quicker. Free's you from
    having to manually type the current branch name in the first push.
    """
    branch = git.current_branch().name
    shell.run('git push -u origin {}'.format(branch))
Example #8
0
def git_branch_delete(branch_name: str):
    """ Delete the given branch.

    Args:
        branch_name (str):
            Name of the branch to delete.
    """
    if branch_name not in git.protected_branches():
        log.info("Deleting branch <33>{}", branch_name)
        shell.run('git branch -d {}'.format(branch_name))
Example #9
0
def test_inherits_existing_env_when_env_is_given(p_popen):
    with patch('os.environ', {'fake1': 'env'}):
        shell.run('hello', env={'fake2': 'arg'})

    p_popen.assert_called_once_with(
        'hello',
        env={
            'fake1': 'env',
            'fake2': 'arg'
        },
        shell=True,
    )
Example #10
0
def git_checkout(branch_name: str, create: bool = False):
    """ Checkout or create a given branch

    Args:
        branch_name (str):
            The name of the branch to checkout or create.
        create (bool):
            If set to **True** it will create the branch instead of checking it
            out.
    """
    log.info("Checking out <33>{}".format(branch_name))
    shell.run('git checkout {} {}'.format('-b' if create else '', branch_name))
Example #11
0
def git_branch_rename(new_name: str):
    """ Rename the current branch

    Args:
        new_name (str):
            New name for the current branch.
    """
    curr_name = git.current_branch(refresh=True).name

    if curr_name not in git.protected_branches():
        log.info("Renaming branch from <33>{}<32> to <33>{}".format(
            curr_name, new_name))
        shell.run('git branch -m {}'.format(new_name))
Example #12
0
def upload(target: str):
    """ Upload the release to a pypi server.

    TODO: Make sure the git directory is clean before allowing a release.

    Args:
        target (str):
            pypi target as defined in ~/.pypirc
    """
    log.info("Uploading to pypi server <33>{}".format(target))
    with conf.within_proj_dir():
        shell.run('python setup.py sdist register -r "{}"'.format(target))
        shell.run('python setup.py sdist upload -r "{}"'.format(target))
Example #13
0
def _get_todo_details(file_path: str, lines: LineRange) -> Tuple[str, str, int]:
    result = shell.run(
        f"git blame {file_path} -L {lines.start},{lines.end} -p",
        capture=True
    )

    re_name = re.compile(r'^author (?P<name>.*)$')
    re_mail = re.compile(r'^author-mail <(?P<mail>[^\s]+)>$')
    re_time = re.compile(r'^author-time (?P<time>\d+)$')
    author_name = 'Not Committed Yet'
    author_email = 'not.committed.yet'
    author_time = int(datetime.now().timestamp())

    for line in result.stdout.splitlines():
        m = re_name.match(line)
        if m:
            author_name = m.group('name')
        else:
            m = re_mail.match(line)
            if m:
                author_email = m.group('mail')
            else:
                m = re_time.match(line)
                if m:
                    author_time = int(m.group('time'))

    return author_name, author_email, author_time
Example #14
0
def start(component: str, exact: str):
    """ Create a new release branch.

    Args:
        component (str):
            Version component to bump when creating the release. Can be *major*,
            *minor* or *patch*.
        exact (str):
            The exact version to set for the release. Overrides the component
            argument. This allows to re-release a version if something went
            wrong with the release upload.
    """
    version_files = versioning.get_version_files()

    develop = conf.get('git.devel_branch', 'develop')
    common.assert_on_branch(develop)

    with conf.within_proj_dir():
        out = shell.run('git status --porcelain', capture=True).stdout
        lines = out.split(os.linesep)
        has_changes = any(not line.startswith('??') for line in lines
                          if line.strip())

    if has_changes:
        log.info("Cannot release: there are uncommitted changes")
        exit(1)

    old_ver, new_ver = versioning.bump(component, exact)

    log.info("Bumping package version")
    log.info("  old version: <35>{}".format(old_ver))
    log.info("  new version: <35>{}".format(new_ver))

    with conf.within_proj_dir():
        branch = 'release/' + new_ver

        hooks.register.call('pre-release-start', branch, old_ver, new_ver)

        common.git_checkout(branch, create=True)

        log.info("Creating commit for the release")
        shell.run('git add {files} && git commit -m "{msg}"'.format(
            files=' '.join(f'"{v.path}"' for v in version_files),
            msg="Releasing v{}".format(new_ver)))

        hooks.register.call('post-release-start', branch, old_ver, new_ver)
Example #15
0
def setup_ci():
    # type: () -> None
    """ Setup AppEngine SDK on CircleCI """
    gcloud_path = shell.run('which gcloud', capture=True).stdout.strip()
    sdk_path = normpath(join(gcloud_path, '../../platform/google_appengine'))
    gcloud_cmd = gcloud_path + ' --quiet'

    if not exists(sdk_path):
        log.info("Installing AppEngine SDK")
        shell.run(
            'sudo {} components install app-engine-python'.format(gcloud_cmd))
    else:
        # Only initialise once. To reinitialise, just build without cache.
        log.info("AppEngine SDK already initialised")

    log.info("Using service account authentication")
    shell.run('{} auth activate-service-account --key-file {}'.format(
        gcloud_cmd, conf.proj_path('ops/client_secret.json')))
Example #16
0
def devserver(port, admin_port, clear):
    # type: (int, int, bool) -> None
    """ Run devserver.

    Args:
        port (int):
            Port on which the app will be served.
        admin_port (int):
            Port on which the admin interface is served.
        clear (bool):
            If set to **True**, clear the datastore on startup.
    """
    admin_port = admin_port or (port + 1)

    args = ['--port={}'.format(port), '--admin_port={}'.format(admin_port)]

    if clear:
        args += ['--clear_datastore=yes']

    with conf.within_proj_dir():
        shell.run('dev_appserver.py . {args}'.format(args=' '.join(args)))
Example #17
0
def check(paths, include, exclude, only_staged, untracked):
    # type: (str, Sequence[str], Sequence[str], bool, bool) -> None
    """ Run mypy and pylint against the current directory."""

    files = types.FilesCollection(
        paths=paths,
        include=['*.py'] + list(include),  # We only want to lint python files.
        exclude=exclude,
        only_staged=only_staged,
        untracked=untracked,
    )

    paths = fs.collect_files(files)
    wrapped_paths = fs.wrap_paths(paths)

    log.info("Paths:            <33>{}", paths)
    log.info("Wrapped paths:    <33>{}", wrapped_paths)

    log.info("Running <35>mypy")
    shell.run('mypy --ignore-missing-imports {}'.format(wrapped_paths))

    log.info("Running <35>pylint")
    shell.run('pylint {}'.format(wrapped_paths))
Example #18
0
def _get_commits_in_range(start_rev: Optional[str],
                          end_rev: Optional[str]) -> List[git.CommitDetails]:
    if not start_rev:
        versions = [x for x in git.tags() if versioning.is_valid(x[1:])]
        start_rev = versions[-1] if versions else ''

    if not end_rev:
        end_rev = 'HEAD'

    cmd = 'git log --format=%H'
    if start_rev and end_rev:
        cmd += f" {start_rev}..{end_rev}"
    elif end_rev:
        cmd += f" {end_rev}"

    hashes = shell.run(cmd, capture=True).stdout.strip().splitlines()
    return [git.CommitDetails.get(h) for h in hashes]
Example #19
0
def check_todos(
    untracked: bool,
    file_path: Optional[str],
    authors: List[str],
    verify_complete: bool,
) -> None:
    repo_path = Path(
        shell.run("git rev-parse --show-toplevel",
                  capture=True).stdout.strip())

    if file_path:
        if file_path != ':commit':
            input_files = frozenset([file_path])
        else:
            input_files = frozenset([
                repo_path / fpath for fpath in (git.staged() + git.unstaged())
            ])
    else:
        input_files = frozenset(
            repo_path / fpath
            for fpath in (parser.get_changed_files(base_branch='master') +
                          git.staged() + git.unstaged()))

    if untracked:
        input_files |= frozenset([(repo_path / fpath)
                                  for fpath in git.untracked()])

    todos = parser.extract_from_files(list(input_files))

    filtered_todos = [
        t for t in todos
        if not authors or any(a.lower() in t.author.lower() for a in authors)
    ]
    _render_todos(filtered_todos)

    if verify_complete and len(filtered_todos) > 0:
        sys.exit(127)
Example #20
0
def test_will_not_exit_if_exit_on_error_is_set_to_False():
    assert shell.run('hello', exit_on_error=False).return_code == 1
Example #21
0
def _find_appengine_sdk():
    # type: () -> str
    gcloud_path = shell.run('which gcloud', capture=True).stdout.strip()
    return normpath(join(gcloud_path, '../../platform/google_appengine'))
Example #22
0
def git_prune():
    """ Prune dead branches. """
    log.info("Pruning")
    shell.run('git fetch --prune origin')
Example #23
0
def test_line_buffered_by_default(p_popen):
    shell.run('hello')

    p_popen.assert_called_once_with('hello', shell=True)
Example #24
0
def get_changed_files(base_branch: str = 'master') -> List[str]:
    result = shell.run(
        f"git diff --name-only HEAD..$(git merge-base HEAD {base_branch})",
        capture=True,
    )
    return result.stdout.splitlines()
Example #25
0
def test_sets_result_success_status_from_retcode():
    result = shell.run('hello', capture=True)

    assert result.succeeded is True
    assert result.failed is False
Example #26
0
def test_if_not_given_set_exit_on_error_to_opposite_of_capture():
    with pytest.raises(SystemExit):
        shell.run('hello')

    assert shell.run('hello', capture=True).return_code == 1
Example #27
0
def test_will_exit_if_exit_on_error_is_set_to_True():
    with pytest.raises(SystemExit):
        shell.run('hello', exit_on_error=True)