Skip to content

Commit

Permalink
Merge branch 'issue_1059-pkg-mv'
Browse files Browse the repository at this point in the history
  • Loading branch information
ajohns committed Apr 14, 2021
2 parents c1869f4 + 1ff265f commit 3030e05
Show file tree
Hide file tree
Showing 13 changed files with 404 additions and 78 deletions.
7 changes: 7 additions & 0 deletions src/rez/cli/_entry_points.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,3 +297,10 @@ def run_rez_pkg_ignore():
check_production_install()
from rez.cli._main import run
return run("pkg-ignore")


@scriptname("rez-mv")
def run_rez_pkg_mv():
check_production_install()
from rez.cli._main import run
return run("mv")
3 changes: 2 additions & 1 deletion src/rez/cli/_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@
"yaml2py": {},
"bundle": {},
"benchmark": {},
"pkg-ignore": {}
"pkg-ignore": {},
"mv": {}
}


Expand Down
3 changes: 2 additions & 1 deletion src/rez/cli/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ def setup_parser(parser, completions=False):
from rez.config import config
from rez.system import system
from rez.shells import get_shell_types
import os

shells = get_shell_types()

Expand Down Expand Up @@ -42,7 +43,7 @@ def setup_parser(parser, completions=False):
help="create a build environment")
parser.add_argument(
"--paths", type=str, default=None,
help="set package search path")
help="set package search path (use %r separator)" % os.pathsep)
parser.add_argument(
"-t", "--time", type=str,
help="ignore packages released after the given time. Supported formats "
Expand Down
77 changes: 77 additions & 0 deletions src/rez/cli/mv.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
'''
Move a package from one repository to another.
'''
from __future__ import print_function


def setup_parser(parser, completions=False):
parser.add_argument(
"--dest-path", metavar="PATH", required=True,
help="package repository to move PKG to.")
parser.add_argument(
"-k", "--keep-timestamp", action="store_true",
help="keep timestamp of source package.")
parser.add_argument(
"-f", "--force", action="store_true",
help="move package even if it isn't relocatable (use at your own risk)")
pkg_action = parser.add_argument(
"PKG",
help="package to move")
parser.add_argument(
"PATH", nargs='?',
help="The repository containing the package. If not specified, this "
"command will present you with a list to select from.")

if completions:
from rez.cli._complete_util import PackageCompleter
pkg_action.completer = PackageCompleter


def list_repos_containing_pkg(pkg_name, pkg_version):
from rez.config import config
from rez.package_repository import package_repository_manager
import sys

# search for package in each searchpath
matching_repos = []

for path in config.packages_path:
repo = package_repository_manager.get_repository(path)
if repo.get_package(pkg_name, pkg_version):
matching_repos.append(repo)

if matching_repos:
print("No action taken. Run again, and set PATH to one of:")
for repo in matching_repos:
print(str(repo))
else:
print("Package not found.", file=sys.stderr)
sys.exit(1)


def command(opts, parser, extra_arg_groups=None):
from rez.vendor.version.requirement import VersionedObject
from rez.packages import get_package_from_repository
from rez.package_move import move_package
import sys

obj = VersionedObject(opts.PKG)

if opts.PATH is None:
list_repos_containing_pkg(obj.name, obj.version)
sys.exit(0)

# find src pkg
src_pkg = get_package_from_repository(obj.name, obj.version, opts.PATH)

if src_pkg is None:
print("Package not found.", file=sys.stderr)
sys.exit(1)

move_package(
package=src_pkg,
dest_repository=opts.dest_path,
keep_timestamp=opts.keep_timestamp,
force=opts.force,
verbose=opts.verbose
)
38 changes: 25 additions & 13 deletions src/rez/cli/pkg-ignore.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
from __future__ import print_function


# TEMP COMMENT PLS REMOVE

def setup_parser(parser, completions=False):
parser.add_argument(
"-u", "--unignore", action="store_true",
help="Unignore a package.")
parser.add_argument(
"-a", "--allow-missing", action="store_true",
help="Allow ignoring of packages that don't exist.")
PKG_action = parser.add_argument(
"PKG", type=str,
help="The exact package to (un)ignore (eg 'foo-1.2.3').")
Expand All @@ -23,6 +24,17 @@ def setup_parser(parser, completions=False):
PKG_action.completer = PackageCompleter


def list_repos():
from rez.config import config
from rez.package_repository import package_repository_manager

print("No action taken. Run again, and set PATH to one of:")

for path in config.packages_path:
repo = package_repository_manager.get_repository(path)
print(str(repo))


def list_repos_containing_pkg(pkg_name, pkg_version):
from rez.config import config
from rez.package_repository import package_repository_manager
Expand All @@ -33,15 +45,8 @@ def list_repos_containing_pkg(pkg_name, pkg_version):

for path in config.packages_path:
repo = package_repository_manager.get_repository(path)

fam = repo.get_package_family(pkg_name)
if fam is None:
continue

for pkg in fam.iter_packages():
if pkg.version == pkg_version:
matching_repos.append(repo)
break
if repo.get_package(pkg_name, pkg_version):
matching_repos.append(repo)

if matching_repos:
print("No action taken. Run again, and set PATH to one of:")
Expand All @@ -60,15 +65,22 @@ def command(opts, parser, extra_arg_groups=None):
obj = VersionedObject(opts.PKG)

if opts.PATH is None:
list_repos_containing_pkg(obj.name, obj.version)
if opts.allow_missing:
list_repos()
else:
list_repos_containing_pkg(obj.name, obj.version)
sys.exit(0)

repo = package_repository_manager.get_repository(opts.PATH)

if opts.unignore:
i = repo.unignore_package(obj.name, obj.version)
else:
i = repo.ignore_package(obj.name, obj.version)
i = repo.ignore_package(
obj.name,
obj.version,
allow_missing=opts.allow_missing
)

if i == 1:
if opts.unignore:
Expand Down
5 changes: 5 additions & 0 deletions src/rez/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ class PackageCopyError(RezError):
pass


class PackageMoveError(RezError):
"""There was a problem moving a package."""
pass


class ContextBundleError(RezError):
"""There was a problem bundling a context."""
pass
Expand Down
11 changes: 8 additions & 3 deletions src/rez/package_copy.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from rez.config import config
from rez.exceptions import PackageCopyError
from rez.package_repository import package_repository_manager
from rez.packages import Variant
from rez.serialise import FileFormat
from rez.utils import with_noop
from rez.utils.base26 import create_unique_base26_symlink
Expand Down Expand Up @@ -137,13 +138,15 @@ def finalize():
new_src_variants = []

for src_variant in src_variants:
existing_variant = dest_pkg_repo.install_variant(
existing_variant_resource = dest_pkg_repo.install_variant(
src_variant.resource,
overrides=overrides,
dry_run=True
)

if existing_variant:
if existing_variant_resource:
existing_variant = Variant(existing_variant_resource)

if overwrite:
if verbose:
print_info("Source variant %s will overwrite %s",
Expand Down Expand Up @@ -206,11 +209,13 @@ def finalize():
overrides_["timestamp"] = int(time.time())

# install the variant into the package definition
dest_variant = dest_pkg_repo.install_variant(
dest_variant_resource = dest_pkg_repo.install_variant(
variant_resource=src_variant.resource,
overrides=overrides_
)

dest_variant = Variant(dest_variant_resource)

if verbose:
print_info("Copied source variant %s to target variant %s",
src_variant, dest_variant)
Expand Down
90 changes: 90 additions & 0 deletions src/rez/package_move.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
from rez.exceptions import PackageMoveError
from rez.package_copy import copy_package
from rez.package_repository import package_repository_manager
from rez.utils.logging_ import print_info
from rez.vendor.six import six


basestring = six.string_types[0]


def move_package(package, dest_repository, keep_timestamp=False, force=False,
verbose=False):
"""Move a package.
Moving a package means copying the package to a destination repo, and
ignoring (ie hiding - not removing) the source package. The package must
not already exist in the destination repo.
Args:
package (`Package`): Package to move.
dest_repository (`PackageRepository` or str): The package repository, or
a package repository path, to move the package into.
keep_timestamp (bool): By default, a newly copied package will get a
new timestamp (because that's when it was added to the target repo).
By setting this option to True, the original package's timestamp
is kept intact.
force (bool): Move the package regardless of its relocatable attribute.
Use at your own risk (there is no guarantee the resulting package
will be functional).
verbose (bool): Verbose mode.
Returns:
`Package`: The newly created package in the destination repo.
"""
def _info(msg, *nargs):
if verbose:
print_info(msg, *nargs)

# get dest repo
if isinstance(dest_repository, basestring):
repo_path = dest_repository
dest_pkg_repo = package_repository_manager.get_repository(repo_path)
else:
dest_pkg_repo = dest_repository

# check that the package doesn't already exist in the dest repo
pkg = dest_pkg_repo.get_package(package.name, package.version)
if pkg:
raise PackageMoveError(
"Package already exists at destination: %s"
% pkg.uri
)

# move the pkg as atomically as possible:
#
# 1. Hide the dest package (even tho it doesn't exist yet)
# 2. Copy the package
# 3. Unhide the dest package
# 4. Hide the src package
#

# 1.
dest_pkg_repo.ignore_package(
package.name, package.version, allow_missing=True)
_info("Ignored %s in %s ahead of time", package.qualified_name, dest_pkg_repo)

try:
# 2.
result = copy_package(
package=package,
dest_repository=dest_pkg_repo,
force=force,
keep_timestamp=keep_timestamp,
verbose=verbose
)
finally:
# 3.
dest_pkg_repo.unignore_package(package.name, package.version)
_info("Unignored %s in %s", package.qualified_name, dest_pkg_repo)

# 4.
package.repository.ignore_package(package.name, package.version)
_info("Ignored %s", package.uri)

# finish up
a_dest_variant = result["copied"][0][1]
dest_pkg = a_dest_variant.parent

_info("Package %s moved to %s", package.uri, dest_pkg.uri)
return dest_pkg
26 changes: 25 additions & 1 deletion src/rez/package_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,26 @@ def iter_variants(self, package_resource):
"""
raise NotImplementedError

def get_package(self, name, version):
"""Get a package.
Args:
name (str): Package name.
version (`Version`): Package version.
Returns:
`PackageResource` or None: Matching package, or None if not found.
"""
fam = self.get_package_family(name)
if fam is None:
return None

for pkg in fam.iter_packages():
if pkg.version == version:
return pkg

return None

def get_package_from_uri(self, uri):
"""Get a package given its URI.
Expand All @@ -194,14 +214,18 @@ def get_variant_from_uri(self, uri):
"""
return None

def ignore_package(self, pkg_name, pkg_version):
def ignore_package(self, pkg_name, pkg_version, allow_missing=False):
"""Ignore the given package.
Ignoring a package makes it invisible to further resolves.
Args:
pkg_name (str): Package name
pkg_version(`Version`): Package version
allow_missing (bool): if True, allow for ignoring a package that
does not exist. This is useful when you want to copy a package
to a repo and you don't want it visible until the copy is
completed.
Returns:
int:
Expand Down
Loading

0 comments on commit 3030e05

Please sign in to comment.