Beispiel #1
0
def auto_upgrade(force, package):
    """
    Auto-upgrade a package
    """
    package = system.PackageSpec(package)
    p = PACKAGERS.resolved(package)
    if not p.current.valid:
        sys.exit("%s is not currently installed" % package)

    ping = system.SETTINGS.meta.full_path(package.dashed, ".ping")
    if not force and runez.file.is_younger(
            ping, system.SETTINGS.version_check_seconds):
        # We checked for auto-upgrade recently, no need to check again yet
        print("Skipping auto-upgrade, checked recently")
        sys.exit(0)

    runez.touch(ping)

    try:
        p.internal_install()

    except SoftLockException:
        print(
            "Skipping auto-upgrade, %s is currently being installed by another process"
            % package)
        sys.exit(0)
Beispiel #2
0
def check(force, verbose, packages):
    """
    Check whether specified packages need an upgrade
    """
    code = 0
    packages = system.SETTINGS.resolved_packages(
        packages) or system.installed_names()
    if not packages:
        print("No packages installed")

    else:
        for name in packages:
            p = PACKAGERS.resolved(name)
            p.refresh_desired(force=force)
            if not p.desired.valid:
                LOG.error(p.desired.representation(verbose))
                code = 1
            elif not p.current.version or not p.current.valid:
                print(
                    p.desired.representation(verbose, note="is not installed"))
                code = 1
            elif p.current.version != p.desired.version:
                print(
                    p.current.representation(verbose,
                                             note="can be upgraded to %s" %
                                             p.desired.version))
                code = 1
            else:
                print(p.current.representation(verbose, note="is installed"))

    sys.exit(code)
Beispiel #3
0
def install(force, packages):
    """
    Install a package from pypi
    """
    packages = system.SETTINGS.resolved_packages(packages)
    for name in packages:
        p = PACKAGERS.resolved(name)
        p.install(force=force)
Beispiel #4
0
def package(build, dist, symlink, relocatable, sanity_check, folder):
    """
    Package a project from source checkout
    """
    build = runez.resolved_path(build)

    root = None
    target_dist = dist
    if target_dist.startswith("root/"):
        # Special case: we're targeting 'root/...' probably for a debian, use target in that case to avoid venv relocation issues
        target = target_dist[4:]
        if os.path.isdir(target):
            root = target_dist[:4]
            target_dist = target
            LOG.debug("debian mode: %s -> %s", dist, target)

    folder = runez.resolved_path(folder)

    if not os.path.isdir(folder):
        sys.exit("Folder %s does not exist" % short(folder))

    system.SETTINGS.set_base(build)

    setup_py = os.path.join(folder, "setup.py")
    if not os.path.exists(setup_py):
        sys.exit("No setup.py in %s" % short(folder))

    with runez.CurrentFolder(folder):
        # Some setup.py's assume their working folder is the folder where they're in
        result = system.run_python(setup_py,
                                   "--name",
                                   fatal=False,
                                   dryrun=False)
        name = result.output
        if result.failed or not name:
            sys.exit("Could not determine package name from %s" %
                     short(setup_py))

    package_spec = system.PackageSpec(name)
    runez.Anchored.add(folder)
    p = PACKAGERS.resolved(package_spec)
    p.build_folder = build
    p.dist_folder = runez.resolved_path(target_dist)
    p.relocatable = relocatable
    p.source_folder = folder
    p.package()

    p.create_symlinks(symlink, root=root)
    p.sanity_check(sanity_check)

    if p.executables:
        overview = "produced: %s" % runez.quoted(p.executables)

    else:
        overview = "package has no entry-points"

    print("Packaged %s successfully, %s" % (short(folder), overview))
    runez.Anchored.pop(folder)
Beispiel #5
0
def install(force, packages):
    """
    Install a package from pypi
    """
    system.setup_audit_log()
    packages = system.resolved_package_specs(packages)
    for name in packages:
        p = PACKAGERS.resolved(name)
        p.install(force=force)
Beispiel #6
0
def test_channel(*_):
    p = PACKAGERS.get(system.VENV_PACKAGER)("foo")
    p.refresh_desired()
    assert p.desired.representation(
        verbose=True
    ) == "foo 1.0 (as venv wrap, channel: stable, source: test:channel.stable.foo)"

    with runez.CaptureOutput(dryrun=True) as logged:
        p.executables = ["foo/bar"]
        assert p.create_symlinks("foo:baz", fatal=False) == 1
        assert "Would symlink /bar <- baz/bar" in logged.pop()
Beispiel #7
0
def list(verbose):
    """
    List installed packages
    """
    packages = system.installed_names()
    if not packages:
        print("No packages installed")

    else:
        for name in packages:
            p = PACKAGERS.resolved(name)
            print(p.current.representation(verbose))
Beispiel #8
0
def list(verbose):
    """
    List installed packages
    """
    packages = system.resolved_package_specs(None, auto_complete=True)
    if not packages:
        print("No packages installed")

    else:
        for name in packages:
            p = PACKAGERS.resolved(name)
            print(p.current.representation(verbose))
Beispiel #9
0
def uninstall(force, packages):
    """
    Uninstall packages
    """
    packages = system.SETTINGS.resolved_packages(packages)
    errors = 0
    for name in packages:
        p = PACKAGERS.resolved(name)
        if not force and not p.current.file_exists:
            errors += 1
            LOG.error("%s was not installed with pickley", name)
            continue

        eps = p.entry_points
        ep_uninstalled = 0
        ep_missed = 0
        meta_deleted = runez.delete(system.SETTINGS.meta.full_path(name),
                                    fatal=False)
        if not eps and force:
            eps = {name: ""}
        if eps and meta_deleted >= 0:
            for entry_point in eps:
                path = system.SETTINGS.base.full_path(entry_point)
                handler = runez.delete if meta_deleted > 0 else uninstall_existing
                r = handler(path, fatal=False)
                if r < 0:
                    ep_missed += 1
                elif r > 0:
                    ep_uninstalled += 1

        if ep_missed or meta_deleted < 0:
            # Error was already reported
            errors += 1
            continue

        if ep_uninstalled + meta_deleted == 0:
            system.inform("Nothing to uninstall for %s" % name)
            continue

        message = "Would uninstall" if runez.DRYRUN else "Uninstalled"
        message = "%s %s" % (message, name)
        if ep_uninstalled > 1:
            message += " (%s entry points)" % ep_uninstalled
        system.inform(message)

    if errors:
        sys.exit(1)
Beispiel #10
0
def test_delivery(temp_base):
    # Test copy folder
    tox = system.PackageSpec("tox")
    deliver = DELIVERERS.get("copy")(tox)
    target = os.path.join(temp_base, "t1")
    source = os.path.join(temp_base, "t1-source")
    source_file = os.path.join(source, "foo")
    runez.touch(source_file)
    deliver.install(target, source)
    assert os.path.isdir(target)
    assert os.path.isfile(os.path.join(target, "foo"))

    # Test copy file
    deliver = DELIVERERS.get("copy")(tox)
    target = os.path.join(temp_base, "t2")
    source = os.path.join(temp_base, "t2-source")
    runez.touch(source)
    deliver.install(target, source)
    assert os.path.isfile(target)

    # Test symlink
    deliver = DELIVERERS.get("symlink")(tox)
    target = os.path.join(temp_base, "l2")
    source = os.path.join(temp_base, "l2-source")
    runez.touch(source)
    deliver.install(target, source)
    assert os.path.islink(target)

    # Test wrapper
    p = PACKAGERS.get(system.VENV_PACKAGER)(tox)
    assert p.create_symlinks(None) == 0
    assert p.create_symlinks("foo", fatal=False) == 0
    p.executables = ["foo"]
    assert p.create_symlinks("foo:bar", fatal=False) == -1
    assert str(p) == "venv tox"
    target = os.path.join(temp_base, "tox")
    source = os.path.join(temp_base, "tox-source")
    runez.touch(source)
    deliver = DELIVERERS.get("wrap")(system.PackageSpec("tox"))
    deliver.install(target, source)
    assert runez.is_executable(target)
Beispiel #11
0
def package(build, dist, symlink, relocatable, sanity_check, folder):
    """
    Package a project from source checkout
    """
    build = runez.resolved_path(build)
    dist = runez.resolved_path(dist)
    folder = runez.resolved_path(folder)

    system.SETTINGS.meta = meta_folder(build)

    if not os.path.isdir(folder):
        sys.exit("Folder %s does not exist" % short(folder))

    setup_py = os.path.join(folder, "setup.py")
    if not os.path.exists(setup_py):
        sys.exit("No setup.py in %s" % short(folder))

    with runez.CurrentFolder(folder):
        # Some setup.py's assume their working folder is the folder where they're in
        name = system.run_python(setup_py, "--name", fatal=False, dryrun=False)
        if not name:
            sys.exit("Could not determine package name from %s" %
                     short(setup_py))

    runez.Anchored.add(folder)
    p = PACKAGERS.resolved(name)
    p.build_folder = build
    p.dist_folder = dist
    p.relocatable = relocatable
    p.source_folder = folder
    p.package()
    p.create_symlinks(symlink)
    p.sanity_check(sanity_check)
    print("Packaged %s successfully, produced: %s" %
          (short(folder), runez.represented_args(p.executables)))
    runez.Anchored.pop(folder)
Beispiel #12
0
def test_special_packages(*_):
    p = PACKAGERS.get("venv")("awscli")
    assert p.get_entry_points() == {"aws": "scripts.aws:main"}
Beispiel #13
0
def test_versions(_, __, temp_base):
    p = PACKAGERS.get("pex")("foo")
    p.pip_wheel = lambda *_: None

    assert p.specced_command() == "pex"
    p.implementation_version = "1.4.5"
    assert p.specced_command() == "pex==1.4.5"
    p.refresh_desired()
    assert p.desired.representation(
        verbose=True
    ) == "foo: can't determine latest version from pypi (channel: latest, source: pypi)"

    with patch("pickley.package.latest_pypi_version",
               return_value="error: test failed"):
        p.refresh_desired()
        assert p.desired.representation() == "foo: test failed"

    system.SETTINGS.cli.contents["channel"] = "stable"
    p.refresh_desired()
    assert p.desired.representation() == "foo: can't determine stable version"

    p.version = None
    assert "can't determine stable version" in verify_abort(p.install)

    # Without a build folder
    assert p.get_entry_points() is None

    # With an empty build fodler
    runez.ensure_folder(p.build_folder, folder=True)
    assert p.get_entry_points() is None

    # With a bogus wheel
    with runez.CaptureOutput() as logged:
        p.version = "0.0.0"
        whl = os.path.join(p.build_folder, "foo-0.0.0-py2.py3-none-any.whl")
        runez.touch(whl)
        assert p.get_entry_points() is None
        assert "Can't read wheel" in logged
        runez.delete(whl)

        p.refresh_desired()
        assert p.desired.channel == "adhoc"
        assert p.desired.source == "cli"
        assert p.desired.version == "0.0.0"
        assert not p.desired.problem
        p.version = None
        p.refresh_desired()
        assert p.desired.problem

    # Ambiguous package() call
    assert "Need either source_folder or version in order to package" in verify_abort(
        p.package)

    # Package bogus folder without a setup.py
    p.source_folder = temp_base
    assert "No setup.py" in verify_abort(p.package)

    # Package with a bogus setup.py
    setup_py = os.path.join(temp_base, "setup.py")
    runez.touch(setup_py)
    assert "Could not determine version" in verify_abort(p.package)

    # Provide a minimal setup.py
    runez.write(
        setup_py,
        "from setuptools import setup\nsetup(name='foo', version='0.0.0')\n")

    # Package project without entry points
    p.get_entry_points = lambda *_: None
    assert "is not a CLI" in verify_abort(p.required_entry_points)
Beispiel #14
0
    metavar="PATH",
    help="Base installation folder to use (default: folder containing pickley)"
)
@click.option("--index", "-i", metavar="PATH", help="Pypi index to use")
@click.option("--config", "-c", metavar="PATH", help="Extra config to load")
@click.option("--python",
              "-P",
              metavar="PATH",
              help="Python interpreter to use")
@click.option("--delivery",
              "-d",
              type=click.Choice(DELIVERERS.names()),
              help="Delivery method to use")
@click.option("--packager",
              "-p",
              help="Packager to use (one of: %s)" % ",".join(PACKAGERS.names())
              )
def main(ctx, debug, dryrun, base, index, config, python, delivery, packager):
    """
    Package manager for python CLIs
    """
    if any(opt in sys.argv
           for opt in ctx.help_option_names):  # pragma: no cover
        return

    if dryrun:
        debug = True

    if base:
        base = runez.resolved_path(base)
        if not os.path.exists(base):
Beispiel #15
0
def uninstall(all, force, packages):
    """
    Uninstall packages
    """
    if packages and all:
        sys.exit(
            "Either specify packages to uninstall, or --all (but not both)")

    if not packages and not all:
        sys.exit("Specify packages to uninstall, or --all")

    if packages and system.PICKLEY in packages:
        sys.exit(
            "Run 'uninstall --all' if you wish to uninstall pickley itself (and everything it installed)"
        )

    system.setup_audit_log()
    package_specs = system.resolved_package_specs(packages, auto_complete=all)
    errors = 0
    for package_spec in package_specs:
        p = PACKAGERS.resolved(package_spec)
        if not force and not p.current.file_exists:
            errors += 1
            LOG.error("%s was not installed with pickley", package_spec)
            continue

        eps = p.entry_points
        ep_uninstalled = 0
        ep_missed = 0
        meta_deleted = runez.delete(system.SETTINGS.meta.full_path(
            package_spec.dashed),
                                    fatal=False)
        if not eps and force:
            eps = {package_spec.dashed: ""}
        if eps and meta_deleted >= 0:
            for entry_point in eps:
                path = system.SETTINGS.base.full_path(entry_point)
                handler = runez.delete if meta_deleted > 0 else uninstall_existing
                r = handler(path, fatal=False)
                if r < 0:
                    ep_missed += 1
                elif r > 0:
                    ep_uninstalled += 1

        if ep_missed or meta_deleted < 0:
            # Error was already reported
            errors += 1
            continue

        if ep_uninstalled + meta_deleted == 0:
            system.inform("Nothing to uninstall for %s" % package_spec)
            continue

        message = "Would uninstall" if runez.DRYRUN else "Uninstalled"
        message = "%s %s" % (message, package_spec)
        if ep_uninstalled > 1:
            message += " (%s entry points)" % ep_uninstalled
        system.inform(message)

    if all and not errors:
        runez.delete(system.SETTINGS.meta.path)

    if errors:
        sys.exit(1)
Beispiel #16
0
def test_install(cli):
    tox = system.SETTINGS.base.full_path("tox")
    p = PACKAGERS.resolved("tox")
    p.refresh_desired()
    tox_version = p.desired.version
    assert not os.path.exists(tox)
    assert runez.first_line(tox) is None

    cli.expect_success("--dryrun -b{base} --delivery wrap install tox",
                       "Would wrap",
                       "Would install tox",
                       base=cli.context)
    cli.expect_success("--dryrun -b{base} --delivery symlink install tox",
                       "Would symlink",
                       "Would install tox",
                       base=cli.context)
    cli.expect_failure("--dryrun -b{base} --delivery foo install tox",
                       "invalid choice: foo",
                       base=cli.context)

    cli.expect_success("--dryrun uninstall /dev/null --force",
                       "Nothing to uninstall")

    runez.touch("foo")
    assert os.path.exists("foo")
    cli.expect_failure("uninstall foo", "foo was not installed with pickley")
    cli.expect_success("uninstall foo --force", "Uninstalled foo")

    assert not os.path.exists("foo")
    assert runez.ensure_folder("foo", folder=True) == 1
    cli.expect_failure("uninstall foo --force",
                       "Can't automatically uninstall")

    cli.expect_failure("-b{base} check tox foo/bar",
                       "is not installed",
                       "can't determine latest version",
                       base=cli.context)
    cli.expect_failure("-b{base} install six",
                       "'six' is not a CLI",
                       base=cli.context)

    # Install tox, but add a few files + a bogus previous entry point to test cleanup
    wep1 = system.SETTINGS.base.full_path("tox-old-entrypoint1")
    tep10 = system.SETTINGS.meta.full_path("tox", "tox-old-entrypoint1-1.0")
    tep11 = system.SETTINGS.meta.full_path("tox", "tox-old-entrypoint1-1.1")
    t00 = system.SETTINGS.meta.full_path("tox", "tox-0.0.0")
    tfoo = system.SETTINGS.meta.full_path("tox", "tox-foo")
    runez.touch(wep1)
    runez.touch(tep10)
    runez.touch(tep11)
    runez.touch(t00)
    runez.touch(tfoo)
    eppath = system.SETTINGS.meta.full_path("tox", ".entry-points.json")
    runez.write(eppath, '["tox-old-entrypoint1", "tox-old-entrypoint2"]\n')
    cli.expect_success("-b{base} --delivery wrap install tox",
                       "Installed tox",
                       base=cli.context)

    # Old entry point removed immediately
    assert not os.path.exists(wep1)

    # Only 1 cleaned up immediately (latest + 1 kept)
    assert not os.path.exists(tep10)
    assert os.path.exists(tep11)
    assert not os.path.exists(t00)
    assert os.path.exists(tfoo)

    assert runez.is_executable(tox)
    output = run_program(tox, "--version")
    assert "tox" in output
    assert tox_version in output

    cli.expect_success("-b{base} auto-upgrade tox",
                       "Skipping auto-upgrade",
                       base=cli.context)
    runez.delete(system.SETTINGS.meta.full_path("tox", ".ping"))
    cli.expect_success("-b{base} auto-upgrade tox",
                       "already installed",
                       base=cli.context)

    version = output.partition(" ")[0]
    cli.expect_success("copy .pickley/tox/tox-%s tox-copy" % version, "Copied")
    cli.expect_success("move tox-copy tox-relocated", "Moved")

    # Verify that older versions and removed entry-points do get cleaned up
    runez.save_json({"install_timeout": 0}, "custom-timeout.json")
    cli.expect_success("-b{base} -ccustom-timeout.json install tox",
                       "already installed",
                       base=cli.context)

    # All cleaned up when enough time went by
    assert not os.path.exists(tep10)
    assert not os.path.exists(tep11)
    assert not os.path.exists(t00)
    assert not os.path.exists(tfoo)

    cli.expect_success("-b{base} check",
                       "tox",
                       "is installed",
                       base=cli.context)
    cli.expect_success(
        "-b{base} check --verbose",
        "tox",
        "is installed (as %s wrap, channel: " % system.VENV_PACKAGER,
        base=cli.context,
    )

    p = PACKAGERS.get(system.VENV_PACKAGER)("tox")
    p.refresh_latest()
    p.latest.version = "10000.0"
    p.latest.save()
    cli.expect_failure("-b{base} check",
                       "tox",
                       "can be upgraded to 10000.0",
                       base=cli.context)

    cli.expect_success("-b{base} -ppex install twine",
                       "Installed twine",
                       base=cli.context)

    cli.expect_success("-b{base} list", "tox", "twine", base=cli.context)
    cli.expect_success("-b{base} list --verbose",
                       "tox",
                       "twine",
                       base=cli.context)

    tmp = os.path.realpath(cli.context)
    assert find_uninstaller(os.path.join(tmp, "tox"))
    assert find_uninstaller(os.path.join(tmp, "twine"))

    cli.expect_success("-b{base} uninstall twine",
                       "Uninstalled twine",
                       base=cli.context)

    runez.delete(p.current._path)
    runez.touch(p.current._path)
    cli.expect_failure("-b{base} check",
                       "tox",
                       "Couldn't read",
                       "is not installed",
                       base=cli.context)

    cli.expect_success("-b{base} uninstall tox",
                       "Uninstalled tox",
                       "entry points",
                       base=cli.context)