Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Package move #1061

Merged
merged 4 commits into from
Apr 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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