def package(pspec, build_folder, dist_folder, requirements, run_compile_all): runez.ensure_folder(build_folder, clean=True) if pspec.python.major < 3: # pragma: no cover abort("Packaging with pex is not supported any more with python2") pex_root = os.path.join(build_folder, "pex-root") tmp = os.path.join(build_folder, "pex-tmp") wheels = os.path.join(build_folder, "wheels") runez.ensure_folder(tmp, logger=False) runez.ensure_folder(wheels, logger=False) pex_venv = PythonVenv(pspec, folder=os.path.join(build_folder, "pex-venv")) pex_venv.pip_install("pex==2.1.75", *requirements) pex_venv.pip_wheel("--cache-dir", wheels, "--wheel-dir", wheels, *requirements) contents = PackageContents(pex_venv, pspec) if contents.entry_points: wheel_path = pspec.find_wheel(wheels) result = [] for name in contents.entry_points: target = os.path.join(dist_folder, name) runez.delete(target) pex_venv.run_python( "-mpex", "-o%s" % target, "--pex-root", pex_root, "--tmpdir", tmp, "--no-index", "--find-links", wheels, # resolver options None if run_compile_all else "--no-compile", # output options "-c%s" % name, # entry point options "--python-shebang", "/usr/bin/env python%s" % pspec.python.major, wheel_path, ) result.append(target) return result
def resolve(self): if not os.path.isdir(self.folder): abort("Folder %s does not exist" % runez.red(runez.short(self.folder))) req = self.requirements if not req: default_req = runez.resolved_path("requirements.txt", base=self.folder) if os.path.exists(default_req): req = [default_req] if req: req = [("-r", runez.resolved_path(r, base=self.folder)) for r in req] req = runez.flattened(req, shellify=True) req.append(self.folder) self.requirements = req self.pspec = PackageSpec(CFG, self.folder) LOG.info("Using python: %s" % self.pspec.python) if self.dist.startswith("root/"): # Special case: we're targeting 'root/...' probably for a debian, use target in that case to avoid venv relocation issues target = self.dist[4:] if os.path.isdir(target): LOG.debug("debian mode: %s -> %s", self.dist, target) self.dist = target parts = self.dist.split("/") if len(parts) <= 2: # Auto-add package name to targets of the form root/subfolder (most typical case) self.dist = os.path.join(self.dist, self.pspec.dashed)
def __init__(self, spec): self.base, _, self.target = spec.partition(":") if not self.base or not self.target: abort("Invalid symlink specification '%s'" % spec) self.base = runez.resolved_path(self.base) self.target = runez.resolved_path(self.target)
def base(what): """Show pickley base folder""" path = CFG.base.path if what == "bootstrap-own-wrapper": # Internal: called by bootstrap script from pickley.delivery import DeliveryMethodWrap pspec = PackageSpec(CFG, PICKLEY, version=__version__) venv = PythonVenv(pspec, create=False) wrap = DeliveryMethodWrap() wrap.install(pspec, venv, {PICKLEY: PICKLEY}) return if what: paths = { "audit": CFG.meta.full_path("audit.log"), "cache": CFG.cache.path, "config": CFG.meta.full_path("config.json"), "meta": CFG.meta.path, } paths["audit.log"] = paths["audit"] paths["config.json"] = paths["config"] path = paths.get(what) if not path: options = [runez.green(s) for s in sorted(paths)] abort("Unknown base folder reference '%s', try one of: %s" % (runez.red(what), ", ".join(options))) print(path)
def perform_install(pspec, is_upgrade=False, force=False, quiet=False): """ Args: pspec (PackageSpec): Package spec to install is_upgrade (bool): If True, intent is an upgrade (not a new install) force (bool): If True, check latest version even if recently checked quiet (bool): If True, don't chatter Returns: (pickley.TrackedManifest): Manifest is successfully installed (or was already up-to-date) """ with SoftLock(pspec): started = time.time() pspec.resolve() skip_reason = pspec.skip_reason(force) if skip_reason: inform("Skipping installation of %s: %s" % (pspec.dashed, runez.bold(skip_reason))) return None manifest = pspec.get_manifest() if is_upgrade and not manifest and not quiet: abort("'%s' is not installed" % runez.red(pspec)) if not pspec.version: desired = pspec.get_desired_version_info(force=force) if desired.problem: action = "upgrade" if is_upgrade else "install" abort("Can't %s %s: %s" % (action, pspec, runez.red(desired.problem))) pspec.version = desired.version if not force and manifest and manifest.version == pspec.version and pspec.is_healthily_installed( ): if not quiet: status = "up-to-date" if is_upgrade else "installed" inform("%s v%s is already %s" % (pspec.dashed, runez.bold(pspec.version), status)) pspec.groom_installation() return manifest setup_audit_log() manifest = PACKAGER.install(pspec) if manifest and not quiet: note = " in %s" % runez.represented_duration(time.time() - started) action = "Upgraded" if is_upgrade else "Installed" if runez.DRYRUN: action = "Would state: %s" % action inform("%s %s v%s%s" % (action, pspec.dashed, runez.bold( pspec.version), runez.dim(note))) if not pspec._pickley_dev_mode: pspec.groom_installation() return manifest
def pip_install(self, *args): """Allows to not forget to state the -i index...""" # bin_marker = ":all:" if runez.SYS_INFO.platform_id.is_macos and runez.SYS_INFO.platform_id.arch == "arm64" else None # r = self.run_pip("install", "-i", self.index, "--no-binary", bin_marker, *args, fatal=False) r = self.run_pip("install", "-i", self.index, *args, fatal=False) if r.failed: message = "\n".join(simplified_pip_error(r.error, r.output)) abort(message) return r
def find_base(): base_path = runez.resolved_path(os.environ.get("PICKLEY_ROOT")) if base_path: if not os.path.isdir(base_path): abort("PICKLEY_ROOT points to non-existing directory %s" % runez.red(base_path)) return runez.resolved_path(base_path) program_path = PickleyConfig.program_path return _find_base_from_program_path(program_path) or os.path.dirname( program_path)
def validate_sanity_check(exe, sanity_check): if not exe or not sanity_check: return None r = runez.run(exe, sanity_check, fatal=False) if r.failed: if does_not_implement_cli_flag(r.output, r.error): return "does not respond to %s" % sanity_check abort("'%s' failed %s sanity check: %s" % (exe, sanity_check, r.full_output)) return runez.first_line(r.output or r.error)
def uninstall(all, packages): """Uninstall packages""" if packages and all: abort("Either specify packages to uninstall, or --all (but not both)") if not packages and not all: abort("Specify packages to uninstall, or --all") if packages and PICKLEY in packages: abort( "Run 'uninstall --all' if you wish to uninstall pickley itself (and everything it installed)" ) setup_audit_log() for pspec in CFG.package_specs(packages): manifest = pspec.get_manifest() if not manifest or not manifest.version: abort("%s was not installed with pickley" % runez.bold(pspec.dashed)) if manifest.entrypoints: for ep in manifest.entrypoints: runez.delete(pspec.exe_path(ep)) runez.delete(pspec.meta_path) action = "Would uninstall" if runez.DRYRUN else "Uninstalled" inform("%s %s" % (action, pspec.dashed)) if all: runez.delete(CFG.base.full_path(PICKLEY)) runez.delete(CFG.meta.path) inform("pickley is now %s" % runez.red("uninstalled"))
def install(self, pspec, venv, entry_points): """ Args: pspec (pickley.PackageSpec): Package spec this installation is for venv (pickley.package.PythonVenv): Virtual env where executables reside (DOT_META/<package>/...) entry_points (dict | list): Full path of executable to deliver (<base>/<entry_point>) """ if not pspec.is_clear_for_installation(): auto_uninstall(pspec.exe_path(pspec.dashed)) try: prev_manifest = pspec.get_manifest() for name in entry_points: src = venv.bin_path(name) dest = pspec.exe_path(name) if runez.DRYRUN: print("Would %s %s -> %s" % (self.short_name, short(dest), short(src))) continue if not os.path.exists(src): abort( "Can't %s %s -> %s: source does not exist" % (self.short_name, short(dest), runez.red(short(src)))) LOG.debug("%s %s -> %s" % (self.action, short(dest), short(src))) self._install(pspec, dest, src) manifest = pspec.save_manifest(entry_points) if not runez.DRYRUN and prev_manifest and prev_manifest.entrypoints: for old_ep in prev_manifest.entrypoints: if old_ep and old_ep not in entry_points: # Remove old entry points that are not in new manifest any more runez.delete(pspec.exe_path(old_ep)) if self.ping: # Touch the .ping file since this is a fresh install (no need to check for upgrades right away) runez.touch(pspec.ping_path) return manifest except Exception as e: abort("Failed to %s %s: %s" % (self.short_name, short(pspec), runez.red(e)))
def install(pspec, ping=True): delivery = DeliveryMethod.delivery_method_by_name(pspec.settings.delivery) delivery.ping = ping args = [pspec.specced] if pspec.folder: args = [pspec.folder] elif pspec._pickley_dev_mode: args = ["-e", pspec._pickley_dev_mode] # pragma: no cover, convenience case for running pickley from .venv/ venv = PythonVenv(pspec) venv.pip_install(*args) contents = PackageContents(venv, pspec) if not contents.entry_points: runez.delete(pspec.meta_path) abort("Can't install '%s', it is %s" % (runez.bold(pspec.dashed), runez.red("not a CLI"))) return delivery.install(pspec, venv, contents.entry_points)
def delivery_method_by_name(cls, name): """ Args: name (str): Name of delivery method Returns: (DeliveryMethod): Associated delivery method """ if name == "wrap": return DeliveryMethodWrap() if name == "symlink": return DeliveryMethodSymlink() return abort("Unknown delivery method '%s'" % runez.red(name))
def auto_uninstall(target): """ Args: target (str): Path to executable to auto-uninstall if needed Returns: Aborts if uninstallation was not possible """ brew, name = find_brew_name(target) if brew and name: result = runez.run(brew, "uninstall", "-f", name, fatal=False, logger=LOG.info) if result.succeeded: LOG.info("Auto-uninstalled brew formula '%s'" % name) return # command = "%s uninstall %s" % (brew, name) abort("'%s' failed, please check" % runez.bold(command)) abort("Can't automatically uninstall %s" % runez.short(target))