Beispiel #1
0
def cli(ctx, service, history, inplace):
    """Generates markdown for the badge of a service. Currently available
    services are: CloudLab, Chameleon, Google Cloud Engine and Popper.
    """
    if history and service:
        raise BadArgumentUsage("--history can't be combined with other flags.")

    remote_url = pu.get_remote_url()

    if not remote_url:
        pu.fail("Failed to infer remote URL for git repository.")

    org, repo = remote_url.split('/')[-2:]

    if history:
        baseurl = pu.read_config().get('badge-server-url',
                                       'http://badges.falsifiable.us')
        try:
            r = requests.get('{}/{}/{}/list'.format(baseurl, org, repo))
            if r.json():
                pu.print_yaml(r.json())
            else:
                pu.info("No records to show")
        except requests.exceptions.RequestException:
            pu.fail("Could not communicate with the badge server")

        sys.exit(0)

    if not service and inplace:
        raise BadArgumentUsage("--inplace must be given with --service")

    if service is None:
        pu.fail('Please specify a service name.')
    if service not in services:
        pu.fail('Unknown service {}.'.format(service))

    if service == 'popper':
        org, repo = remote_url.split('/')[-2:]
        markup = '[![{}]({})]({})'.format(
            services[service][0], services[service][1].format(org, repo),
            services[service][2])
    else:
        markup = '[![{}]({})]({})'.format(*services[service])

    if not inplace:
        pu.info(markup)
        sys.exit(0)

    try:
        os.chdir(pu.get_project_root())
        with open('README.md', 'r+') as f:
            content = f.read()
            f.seek(0, 0)
            f.write(markup + '\n\n' + content)
    except IOError as e:
        if e.errno == ENOENT:
            pu.fail("README.md does not exist at the root of the project")
Beispiel #2
0
def cli(ctx, name, stages, envs, existing, infer_stages):
    """Initializes a repository or a pipeline. Without an argument, this
    command initializes a popper repository. If an argument is given, a
    pipeline or paper folder is initialized. If the given name is 'paper',
    then a 'paper' folder is created. Otherwise, a pipeline named NAME is
    created and initialized inside the 'pipelines' folder.

    By default, the stages of a pipeline are: setup, run, post-run, validate
    and teardown. To override these, the `--stages` flag can be provided, which
    expects a comma-separated list of stage names.

    The teardown stage is to be provided at the end if the --stages flag is
    being used.

    If the --existing flag is given, the NAME argument is treated as a path to
    a folder, which is assumed to contain bash scripts. --stages must be given.
    """

    # check if the the teardown stage is the last stage of the pipeline
    if stages and 'teardown' in stages and stages.split(',')[-1] != 'teardown':
        raise BadArgumentUsage(
            '--stages = Teardown should be the last stage.' +
            ' Consider renaming it or putting it at the end.')

    project_root = pu.get_project_root()

    # init repo
    if name is None:
        initialize_repo(project_root)
        return

    if not pu.is_popperized():
        pu.fail("Repository has not been popperized yet. See 'init --help'")

    if isdir(os.path.join(project_root, name)) and existing:
        # existing pipeline
        abs_path = os.path.join(project_root, name)
        relative_path = name
        if infer_stages:
            stages = ",".join(
                map(lambda x: x[:-3], sorted(glob.glob1(abs_path, '*.sh'))))
        else:
            initialize_existing_pipeline(abs_path, stages, envs)
    elif name == 'paper':
        # create a paper pipeline
        abs_path = os.path.join(project_root, 'paper')
        relative_path = os.path.join('paper')
        initialize_paper(abs_path, envs)
    else:
        # new pipeline
        abs_path = os.path.join(project_root, 'pipelines', name)
        relative_path = os.path.join('pipelines', name)
        initialize_new_pipeline(abs_path, stages, envs)

    pu.update_config(name, stages, envs, relative_path)

    pu.info('Initialized pipeline ' + name, fg='blue', bold=True)
Beispiel #3
0
def cli(ctx, pipeline, folder, branch):
    """Add a pipeline to your repository from the existing popperized
    repositories on github. The pipeline argument is provided as owner/repo/
    pipeline. For example, 'popper add popperized/quiho-popper/single-node'
    adds the 'single-node' pipeline from the 'quiho-popper' repository from the
    'popperized' organization.
    """
    if len(pipeline.split('/')) != 3:
        raise BadArgumentUsage(
            "Bad pipeline name. See 'popper add --help' for more info.")

    owner, repo, pipe_name = pipeline.split('/')

    config = pu.read_config()

    if pipe_name in config['pipelines']:
        pu.fail("Pipeline {} already in repo.".format(pipe_name))

    project_root = pu.get_project_root()
    pipelines_dir = os.path.join(project_root, folder)

    if not os.path.exists(pipelines_dir):
        os.mkdir(pipelines_dir)

    gh_url = 'https://github.com/{}/{}/'.format(owner, repo)
    gh_url += 'archive/{}.tar.gz'.format(branch)

    pu.info("Downloading pipeline {}... ".format(pipe_name))

    r = pu.make_gh_request(
        gh_url,
        msg="Unable to fetch the pipeline. Please check if the name"
        " of the pipeline is correct and the internet is connected"
    )

    # Downloading and extracting the tarfile
    with tarfile.open(
            mode='r:gz', fileobj=BytesIO(r.content)) as t:
        t.extractall()

    os.rename('{}-{}/pipelines/{}'.format(
        repo, branch, pipe_name), os.path.join(folder, pipe_name))
    shutil.rmtree('{}-{}'.format(repo, branch))

    pu.info("Updating popper configuration... ")

    repo_config = get_config(owner, repo)

    config['pipelines'][pipe_name] = repo_config['pipelines'][pipe_name]
    config['pipelines'][pipe_name]['path'] = os.path.join(folder, pipe_name)

    pu.write_config(config)
    pu.info("Pipeline {} has been added successfully.".format(pipe_name),
            fg="green")
Beispiel #4
0
def cli(ctx, pipeline, add, rm, ls):
    """Manipulates the environments that are associated to a pipeline. An
    environment is a docker image where a pipeline runs when 'popper run' is
    executed. The 'host' environment is a special case that corresponds to
    the running directly on the environment where the 'popper' command runs,
    i.e. running directly on the host without docker. When a new pipeline is
    created using, the default environment is 'host' (see 'popper init --help'
    for more).

    Examples:

      popper env mypipeline # show environments for pipeline

      popper env mypipeline --add ubuntu-xenial,centos-7.2

      popper env mypipeline --rm host
    """
    config = pu.read_config()

    if not add and not rm and not ls:
        if not pipeline:
            raise BadArgumentUsage('Expecting name of a pipeline')

        if pipeline not in config['pipelines']:
            pu.fail("Pipeline '{}' not found in .popper.yml".format(pipeline))

        pu.print_yaml(config['pipelines'][pipeline]['envs'], fg='yellow')
        sys.exit(0)

    if add:
        config['pipelines'][pipeline]['envs'] += add.split(',')

    if rm:
        for e in rm.split(','):
            config['pipelines'][pipeline]['envs'].remove(e)

    if ls:
        try:
            response = requests.get("https://hub.docker.com/v2/repositories/"
                                    "falsifiable/poppercheck/tags")
            environments = []
            for result in response.json()['results']:
                environments.append(result['name'])
            pu.info('environments:')
            pu.print_yaml(environments)

        except requests.exceptions.RequestException as e:
            click.echo(click.style("Error: " + str(e), fg='red'), err=True)

    pu.write_config(config)
Beispiel #5
0
def cli(ctx, pipeline):
    """Displays the information related to a pipeline.
    It gives details about the pipeline name, version,
    and contents of the pipeline.

    Examples:
      popper info popperized/quiho-popper/single-node
    """
    pipeline = pipeline.split('/')
    # checking the validity of the provided arguments
    if len(pipeline) != 3:
        raise BadArgumentUsage(
            "Bad pipeline name. See 'popper info --help' for more info.")

    get_info(pipeline)
Beispiel #6
0
def cli(ctx, pipeline, full, update_cache):
    """Displays the description of a pipeline hosted on Github. The
    format for specifying pipelines is <org>/<repo>/<pipeline>. For example:

      popper info popperized/quiho-popper/single-node

    By default, this command searches information from the pipeline metadata
    cache. To recreate it (fetch from Github), pass the --update-cache flag.
    """
    orgrepopipe = pipeline.split('/')

    if len(orgrepopipe) != 3:
        raise BadArgumentUsage(
            "Bad name format. See 'popper info --help' for more info.")

    org, repo, name = orgrepopipe[0:]

    meta = pu.fetch_pipeline_metadata(skip_update=(not update_cache))

    pipe_meta = meta[org][repo]['pipelines'].get(name, None)

    if not pipe_meta:
        pu.fail('Unable to find metadata for given pipeline')

    if not pipe_meta['readme']:
        pu.info('This pipeline does not have a README file associated with it')
        sys.exit(0)

    pu.info('')

    if full:
        pu.info(pipe_meta['readme'])
    else:
        readme_lines = pipe_meta['readme'].split('\n')
        if len(readme_lines) <= 2:
            pu.info(pipe_meta['readme'])
        else:
            for l in readme_lines[2:]:
                if not l:
                    break
                pu.info(l)
    pu.info('')
Beispiel #7
0
def cli(ctx, keywords, skip_update, add, rm, ls, include_readme):
    """Searches for pipelines on GitHub matching the given keyword(s).

    The list of repositories or organizations scraped for Popper pipelines is
    specified in the 'popperized' list in the .popper.yml file. By default,
    https://github.com/popperized is added to the configuration.

    If no keywords are specified, a list of all the pipelines from all
    organizations (in the .popper.yml file) and repositories will be returned.

    Example:

        popper search quiho

    would result in:

        popperized/quiho-popper

    To add or remove orgs/repos to/from the 'popperized' ,
    use the --add and --rm flags while searching.

        popper search --add org/repo

    To remove an organization/person do:

        popper search --rm org/repo

    To view the list repositories that are available to the search command:

        popper search --ls

    """
    if (rm or add or ls) and (keywords):
        raise BadArgumentUsage(
            "'add', 'rm' and 'ls' flags cannot be combined with others.")

    project_root = pu.get_project_root()

    config = pu.read_config()
    popperized_list = config['popperized']

    if add:
        add = 'github/' + add
        if add not in popperized_list:
            popperized_list.append(add)

        config['popperized'] = popperized_list
        pu.write_config(config)
        sys.exit(0)

    if rm:
        rm = 'github/' + rm
        if rm in popperized_list:
            popperized_list.remove(rm)

        config['popperized'] = popperized_list
        pu.write_config(config)
        sys.exit(0)

    result = []  # to store the result of the search query as a list

    if ls:
        for p in popperized_list:
            if p.count('/') == 1:
                org_name = p.split('/')[1]
                org_url = ('https://api.github.com/users/{}/repos')
                org_url = org_url.format(org_name)

                response = pu.make_gh_request(org_url)
                repos = response.json()
                temp = [r["full_name"] for r in repos]
                result.extend(temp)
            else:
                result.extend(p[7:])

        if len(result) > 0:
            pu.info("The list of available poppperized repositories are:\n")
            pu.print_yaml(result)
            sys.exit()
        else:
            fail_msg = "There are no popperized repositores available"
            "for search. Use the --add flag to add an org/repo."

            pu.fail(fail_msg)

        sys.exit(0)

    search_params = {}

    if not keywords:  # checks if the query is empty or not
        search_params['empty_query'] = True
    else:
        search_params['empty_query'] = False

    cache_dir = os.path.join(project_root, '.cache')

    search_params["keywords"] = keywords
    search_params["cache_dir"] = cache_dir
    search_params["skip_update"] = True if skip_update else False
    search_params["in_readme"] = True if include_readme else False

    if not os.path.exists(cache_dir):
        os.makedirs(cache_dir)

    for popperized in popperized_list:
        if popperized.count('/') == 1:
            # it is an organization
            org_name = popperized.split('/')[1]

            repos = ""

            if not skip_update:
                org_url = (
                    'https://api.github.com/users/{}/repos'.format(org_name))

                response = pu.make_gh_request(org_url)

                with open(os.path.join(cache_dir, org_name + '_repos.json'),
                          'w') as f:
                    json.dump(response.json(), f)

            try:
                with open(os.path.join(cache_dir, org_name + '_repos.json'),
                          'r') as f:
                    repos = json.load(f)
            except FileNotFoundError:
                pu.fail('No cached metadata has been downloaded')

            with click.progressbar(
                    repos,
                    show_eta=False,
                    label='Searching in ' + org_name,
                    bar_template='[%(bar)s] %(label)s | %(info)s',
                    show_percent=True) as bar:

                for r in bar:
                    if search_params["empty_query"]:
                        temp = ' {}/{}'\
                            .format(org_name, r['name'])

                        result.append(temp)

                    elif l_distance(r["name"].lower(), keywords.lower()) < 1:
                        temp = ' {}/{}' \
                            .format(org_name, r['name'])
                        result.append(temp)

                    else:
                        search_params["repo_url"] = r["url"]
                        search_params["uname"] = org_name
                        result.extend(search_pipeline(search_params))

        else:
            # it is a repository
            user, repo = popperized.split('/')[1:]
            repo_url = ('https://api.github.com/repos/{}/{}'.format(
                user, repo))

            search_params["repo_url"] = repo_url
            search_params["uname"] = user

            pu.info("Searching in repository : {}".format(repo))
            result.extend(search_pipeline(search_params))

    if len(result) != 0:
        pu.info("\nSearch results:\n", fg="green")
        for res in result:
            pu.info("> " + res + "\n")

        if search_params["in_readme"]:
            pu.info("Use popper info command to view the"
                    " details of a pipeline. See popper info --"
                    "help")
    else:
        pu.fail("Unable to find any matching pipelines")
Beispiel #8
0
def cli(ctx, keywords, skip_update, add, rm, ls, include_readme):
    """Searches for pipelines on Github matching the given keyword(s).

    The list of repositories or organizations scraped for pipelines is
    specified in the 'popperized' list in the .popper.yml file. By default,
    https://github.com/popperized is added to the list.

    If no keywords are specified, a list of all the pipelines from all
    organizations (in the .popper.yml file) and repositories will be shown.

    This commands makes use of Github's API, which has a limit on the number of
    requests per hour that an unauthenticated user can make. If you reach this
    limit, you can provide a Github API token via a POPPER_GITHUB_API_TOKEN
    environment variable. If defined, this variable is used to obtain the token
    when executing HTTP requests.

    Example:

        popper search quiho

    Would result in:

        popperized/quiho-popper/single-node

    The format of search output is <org>/<repo>/<pipeline-name>. To add
    organizations or repositories to the list of pipeline sources:

        popper search --add org/repo
        popper search --add entireorg

    To remove one:

        popper search --rm org/repo
        popper search --rm entireorg

    To view the list repositories that are available to the search command:

        popper search --ls
    """
    if ((rm and add) or (rm and ls) or (add and ls)):
        raise BadArgumentUsage("Only one of 'add', 'rm' and 'ls' accepted.")

    if (rm or add or ls) and keywords:
        raise BadArgumentUsage("Search cannot be combined with other flags.")

    config = pu.read_config()
    sources = pu.get_search_sources(config)

    if add:
        if len(add.split('/')) > 2:
            pu.fail("Bad source naming format. See 'popper search --help'.")
        if add in sources:
            pu.info('{} is already a search source.'.format(add))
            sys.exist(0)

        sources.append(add)

        config['search_sources'] = sources
        pu.write_config(config)
        sys.exit(0)

    if rm:
        if rm not in sources:
            pu.info("'{}' is not a search source.".format(rm))
            sys.exit(0)

        sources.remove(rm)

        config['search_sources'] = sources
        pu.write_config(config)
        sys.exit(0)

    if len(sources) == 0:
        pu.fail('No source for popper pipelines defined! Add one first.')

    pipeline_meta = pu.fetch_pipeline_metadata(skip_update)
    result = search_pipelines(pipeline_meta, keywords, include_readme)

    pu.info('Matching pipelines:')
    pu.print_yaml(result)
Beispiel #9
0
def cli(ctx, pipeline, folder, branch):
    """Add a pipeline to your repository from the existing popperized
    repositories on github. The pipeline argument is provided as
    <org>/<repo>/<pipeline>. For example:

      popper add popperized/quiho-popper/single-node

    The above adds the 'single-node' pipeline from the 'quiho-popper'
    repository from the 'popperized' organization.

    This commands makes use of Github's API, which has a limit on the number of
    requests per hour that an unauthenticated user can make. If you reach this
    limit, you can provide a Github API token via a POPPER_GITHUB_API_TOKEN
    environment variable. If defined, this variable is used to obtain the token
    when executing HTTP requests.
    """
    if len(pipeline.split('/')) != 3:
        raise BadArgumentUsage(
            "Bad pipeline name. See 'popper add --help' for more info.")

    owner, repo, pipe_name = pipeline.split('/')

    new_pipe_name, folder = pu.get_name_and_path_for_new_pipeline(
        folder, pipe_name)

    config = pu.read_config()

    if new_pipe_name in config['pipelines']:
        pu.fail("Pipeline {} already in repo.".format(new_pipe_name))

    project_root = pu.get_project_root()

    pipelines_dir = os.path.join(project_root, folder)

    if not os.path.exists(pipelines_dir):
        try:
            os.makedirs(pipelines_dir)
        except (OSError, IOError) as e:
            pu.fail("Could not create the necessary path.\n")
    elif len(os.listdir(pipelines_dir)) != 0:
        pu.fail("The path already exists and is not empty.")

    gh_url = 'https://github.com/{}/{}/'.format(owner, repo)
    gh_url += 'archive/{}.tar.gz'.format(branch)

    pu.info("Downloading pipeline {} as {}...".format(pipe_name,
                                                      new_pipe_name))

    r = pu.make_gh_request(
        gh_url,
        msg="Unable to fetch the pipeline. Please check if the name"
        " of the pipeline is correct and the internet is connected")

    # Downloading and extracting the tarfile
    with tarfile.open(mode='r:gz', fileobj=BytesIO(r.content)) as t:
        t.extractall()

    try:
        os.rename('{}-{}/pipelines/{}'.format(repo, branch, pipe_name),
                  pipelines_dir)
    except OSError:
        pu.fail("Could not rename {} to {}.".format(
            '{}-{}/pipelines/{}'.format(repo, branch, pipe_name),
            pipelines_dir))
    finally:
        shutil.rmtree('{}-{}'.format(repo, branch))

    pu.info("Updating popper configuration... ")

    repo_config = get_config(owner, repo)

    config['pipelines'][new_pipe_name] = repo_config['pipelines'][pipe_name]
    config['pipelines'][new_pipe_name]['path'] = folder

    pu.write_config(config)
    pu.info("Pipeline {} has been added successfully.".format(new_pipe_name),
            fg="green")