Example #1
0
def cli(path, ignore, exit):
    """conda-verify is a tool for validating conda packages and recipes.

    To validate a package:\n
    $  conda-verify path/to/package.tar.bz2

    To validate a recipe:\n
    $  conda-verify path/to/recipe_directory/
    """
    verifier = Verify()
    if ignore:
        ignore = ignore.split(',')

    meta_file = os.path.join(path, 'meta.yaml')
    if os.path.isfile(meta_file):
        print('Verifying {}...'.format(meta_file))
        for cfg in iter_cfgs():
            meta = render_metadata(path, cfg)
            if meta.get('build', {}).get('skip', '').lower() != 'true':
                verifier.verify_recipe(rendered_meta=meta,
                                       recipe_dir=path,
                                       checks_to_ignore=ignore,
                                       exit_on_error=exit)

    elif path.endswith(('.tar.bz2', '.tar')):
        print('Verifying {}...'.format(path))
        verifier.verify_package(path_to_package=path,
                                checks_to_ignore=ignore,
                                exit_on_error=exit)
Example #2
0
def main():
    p = OptionParser(
        usage="usage: %prog [options] <path to recipes or packages>",
        description="tool for (passively) verifying conda recipes and conda "
        "packages for the Anaconda distribution")

    p.add_option('-e', "--exit", help="on error exit", action="store_true")

    p.add_option('-p', "--pedantic", action="store_true")

    p.add_option('-q', "--quiet", action="store_true")

    p.add_option('-V',
                 '--version',
                 help="display the version being used and exit",
                 action="store_true")

    opts, args = p.parse_args()
    verbose = not opts.quiet
    if opts.version:
        from conda_verify import __version__
        print('conda-verify version:', __version__)
        return

    verifier = Verify()
    for path in args:
        if isfile(join(path, 'meta.yaml')):
            if verbose:
                print("==> %s <==" % path)
            for cfg in iter_cfgs():
                meta = render_metadata(path, cfg)
                try:
                    verifier.verify_recipe(pedantic=opts.pedantic,
                                           rendered_meta=meta,
                                           recipe_dir=path)
                except RecipeError as e:
                    sys.stderr.write("RecipeError: %s\n" % e)
                    if opts.exit:
                        sys.exit(1)

        elif path.endswith('.tar.bz2'):
            if verbose:
                print("==> %s <==" % path)
            try:
                verifier.verify_package(pedantic=opts.pedantic,
                                        path_to_package=path,
                                        verbose=verbose)
            except PackageError as e:
                sys.stderr.write("PackageError: %s\n" % e)
                if opts.exit:
                    sys.exit(1)

        else:
            if verbose:
                print("Ignoring: %s" % path)
Example #3
0
def test_invalid_test_files(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_test_files')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2124] Found file "test-data.txt" in meta.yaml that doesn\'t exist' in error
Example #4
0
def test_invalid_license_family(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_license_family')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2122] Found invalid license family "The Extra License"' in error
Example #5
0
def test_invalid_source_hash(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_source_hash')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2119] Found invalid hash "abc123" in meta.yaml' in error
Example #6
0
def test_invalid_about_url(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_about_url')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2118] Found invalid URL "www.continuum.io" in meta.yaml' in error
Example #7
0
def test_invalid_about_summary(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_about_summary')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2117] Found summary with length greater than 80 characters' in error
Example #8
0
def test_invalid_build_number_negative(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_build_number_negative')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2108] Build number in info/index.json cannot be a negative integer' in error
Example #9
0
def test_invalid_package_version_sequence(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_package_version_sequence')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2106] Found invalid sequence "._" in package version' in error
Example #10
0
def test_invalid_package_version_prefix(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_package_version_prefix')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2105] Found invalid package version "_1.0.0rc3" in meta.yaml' in error
Example #11
0
def test_invalid_package_name(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_package_name')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2102] Found invalid package name "some_package." in meta.yaml' in error
Example #12
0
def test_invalid_package_field(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_package_field')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2109] Found invalid section "extra_field"' in error
Example #13
0
def test_invalid_test_file_path(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_test_file_path')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2123] Found file "../test-data.txt" listed outside recipe directory' in error
Example #14
0
def test_invalid_multiple_source_field_key(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_multiple_sources')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2110] Found invalid field "gti_url" in section "source"' in error
Example #15
0
def test_invalid_requirements_field_key(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_requirements_field_key')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2110] Found invalid field "extra_field" in section "requirements"' in error
Example #16
0
def test_invalid_run_requirement_name(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_run_requirement_name')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2112] Found invalid run requirement "python@#"' in error
Example #17
0
def test_no_package_name(recipe_dir):
    recipe = os.path.join(recipe_dir, 'no_package_name')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2101] Missing package name in meta.yaml' in error
Example #18
0
def test_invalid_sources(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_sources')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2121] Found both git_branch and git_tag in meta.yaml source field' in error
Example #19
0
def test_invalid_build_requirement_version(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_build_requirement_version')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2114] Found invalid dependency "setuptools >= 3.4 < 3045" in meta.yaml' in error
Example #20
0
def test_invalid_outputs(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_output')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2110] Found invalid field "srcitp" in section "outputs"' in error
Example #21
0
def test_conda_forge_example_recipe(recipe_dir):
    recipe = os.path.join(recipe_dir, 'conda_forge')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert '[C2126] Found conda-forge comment in meta.yaml file' in error
Example #22
0
def test_duplicate_version_specifications(recipe_dir):
    recipe = os.path.join(recipe_dir, 'duplicate_version_specs')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert "[C2116] Found duplicate run requirements: ['python', 'python']" in error
Example #23
0
def test_invalid_dir_content_filesize(recipe_dir):
    recipe = os.path.join(recipe_dir, 'invalid_dir_content_filesize')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert any('[C2125] Found disallowed file with extension' in e
               for e in error)
    assert any('test.tar.bz2' in e for e in error)
Example #24
0
def test_duplicate_build_requirements(recipe_dir):
    recipe = os.path.join(recipe_dir, 'duplicate_build_requirements')
    metadata = utilities.render_metadata(recipe, None)

    with pytest.raises(RecipeError):
        Verify.verify_recipe(rendered_meta=metadata,
                             recipe_dir=recipe,
                             exit_on_error=True)

    package, error = Verify.verify_recipe(rendered_meta=metadata,
                                          recipe_dir=recipe,
                                          exit_on_error=False)

    assert (
        "[C2115] Found duplicate build requirements: ['python', 'python']"
        in error or
        "[C2115] Found duplicate build requirements: ['python 3.6.*', 'python 3.6.*']"
        in error)
Example #25
0
def build_tree(recipe_list, config, build_only=False, post=False, notest=False,
               need_source_download=True, need_reparse_in_env=False):

    to_build_recursive = []
    recipe_list = deque(recipe_list)

    if on_win:
        trash_dir = os.path.join(os.path.dirname(sys.executable), 'pkgs', '.trash')
        if os.path.isdir(trash_dir):
            # We don't really care if this does a complete job.
            #    Cleaning up some files is better than none.
            subprocess.call('del /s /q "{0}\\*.*" >nul 2>&1'.format(trash_dir), shell=True)
        # delete_trash(None)

    already_built = set()
    extra_help = ""
    while recipe_list:
        # This loop recursively builds dependencies if recipes exist
        if build_only:
            post = False
            notest = True
            config.anaconda_upload = False
        elif post:
            post = True
            notest = True
            config.anaconda_upload = False
        else:
            post = None

        recipe = recipe_list.popleft()
        if hasattr(recipe, 'config'):
            metadata = recipe
            recipe_config = metadata.config
            # this code is duplicated below because we need to be sure that the build id is set
            #    before downloading happens - or else we lose where downloads are
            if recipe_config.set_build_id:
                recipe_config.compute_build_id(metadata.name(), reset=True)
            recipe_parent_dir = ""
            to_build_recursive.append(metadata.name())
        else:
            recipe_parent_dir = os.path.dirname(recipe)
            recipe = recipe.rstrip("/").rstrip("\\")
            recipe_config = config
            to_build_recursive.append(os.path.basename(recipe))

            #    before downloading happens - or else we lose where downloads are
            if recipe_config.set_build_id:
                recipe_config.compute_build_id(os.path.basename(recipe), reset=True)
            metadata, need_source_download, need_reparse_in_env = render_recipe(recipe,
                                                                    config=recipe_config)
        if not getattr(config, "noverify", False):
            verifier = Verify()
            ignore_scripts = context.ignore_recipe_verify_scripts if \
                context.ignore_recipe_verify_scripts else None
            run_scripts = context.run_recipe_verify_scripts if \
                context.run_recipe_verify_scripts else None
            verifier.verify_recipe(ignore_scripts=ignore_scripts, run_scripts=run_scripts,
                                   rendered_meta=metadata.meta, recipe_dir=metadata.path)
        try:
            with recipe_config:
                ok_to_test = build(metadata, post=post,
                                   need_source_download=need_source_download,
                                   need_reparse_in_env=need_reparse_in_env,
                                   config=recipe_config)
                if not notest and ok_to_test:
                    test(metadata, config=recipe_config)
        except (NoPackagesFound, NoPackagesFoundError, Unsatisfiable, CondaValueError) as e:
            error_str = str(e)
            skip_names = ['python', 'r']
            add_recipes = []
            # add the failed one back in at the beginning - but its deps may come before it
            recipe_list.extendleft([recipe])
            for line in error_str.splitlines():
                if not line.startswith('  - '):
                    continue
                pkg = line.lstrip('  - ').split(' -> ')[-1]
                pkg = pkg.strip().split(' ')[0]

                if pkg in to_build_recursive:
                    raise RuntimeError("Can't build {0} due to unsatisfiable dependencies:\n"
                                       .format(recipe) + error_str + "\n" + extra_help)

                if pkg in skip_names:
                    to_build_recursive.append(pkg)
                    extra_help = """Typically if a conflict is with the Python or R
packages, the other package needs to be rebuilt
(e.g., a conflict with 'python 3.5*' and 'x' means
'x' isn't build for Python 3.5 and needs to be rebuilt."""

                recipe_glob = glob(os.path.join(recipe_parent_dir, pkg))
                if recipe_glob:
                    for recipe_dir in recipe_glob:
                        print(error_str)
                        print(("Missing dependency {0}, but found" +
                                " recipe directory, so building " +
                                "{0} first").format(pkg))
                        add_recipes.append(recipe_dir)
                else:
                    raise RuntimeError("Can't build {0} due to unsatisfiable dependencies:\n"
                                       .format(recipe) + error_str + "\n\n" + extra_help)
            recipe_list.extendleft(add_recipes)

        # outputs message, or does upload, depending on value of args.anaconda_upload
        if post in [True, None]:
            output_file = bldpkg_path(metadata, config=recipe_config)
            handle_anaconda_upload(output_file, config=recipe_config)
            already_built.add(output_file)