diff options
Diffstat (limited to 'bin/just-lock.py')
-rwxr-xr-x | bin/just-lock.py | 106 |
1 files changed, 101 insertions, 5 deletions
diff --git a/bin/just-lock.py b/bin/just-lock.py index 3cc6659d..997f058f 100755 --- a/bin/just-lock.py +++ b/bin/just-lock.py @@ -70,6 +70,12 @@ GIT_NOBODY_ENV: Dict[str, str] = { "GIT_CONFIG_SYSTEM": "/dev/null", } +SPECIAL_PRAGMA_TO_CAS_RESOLVE_MAP: Dict[str, str] = { + "ignore": "ignore", + "resolve-partially": "tree-upwards", + "resolve-completely": "tree-all" +} + class ObjectType(Enum): FILE = 1 @@ -2074,6 +2080,96 @@ def clone_repo(repos: Json, known_repo: str, deps_chain: List[str], ("\n".join(["\t%s" % (x, ) for x in sources]), )) return commit + def process_archive(repository: Json, repo_type: str, *, clone_to: str, + fail_context: str) -> str: + """Process an archive-like repository and return its content id.""" + # Parse entries not covered in fetch method to check for early fail + subdir: Optional[str] = repository.get("subdir", None) + if subdir is not None: + if not isinstance(subdir, str): + fail( + fail_context + + "Expected field \"subdir\" to be a string, but found:\n%r" % + (json.dumps(subdir, indent=2), )) + subdir = os.path.normpath(subdir) + if os.path.isabs(subdir) or subdir.startswith(".."): + fail( + fail_context + + "Expected field \"subdir\" to be a relative non-upward path, but found:\n%r" + % (json.dumps(subdir, indent=2), )) + if subdir == ".": + subdir = None # treat as if missing + # Fetch the archive + content = archive_fetch_with_parse(repository, + fail_context=fail_context) + # Stage the content; first resolve special entries, if needed, then keep + # the relevant subdir, if given + special_pragma: Optional[str] = repository.get("pragma", + {}).get("special", None) + + if (special_pragma not in SPECIAL_PRAGMA_TO_CAS_RESOLVE_MAP.keys() + and subdir is None): + # Unpack directly to clone location + unpack_archive(content, + archive_type=repo_type, + unpack_to=clone_to, + fail_context=fail_context) + else: + # Unpack to a temporary dir + workdir: str = create_tmp_dir(type="archive-unpack") + srcdir: str = os.path.join(workdir, "src") + unpack_archive(content, + archive_type=repo_type, + unpack_to=srcdir, + fail_context=fail_context) + + move_from_dir: str = srcdir + if (special_pragma is not None and special_pragma + in SPECIAL_PRAGMA_TO_CAS_RESOLVE_MAP.keys()): + # Resolve the tree according to the pragma + resolve_special_arg = SPECIAL_PRAGMA_TO_CAS_RESOLVE_MAP[ + special_pragma] + resolved_tree = run_cmd( + g_LAUNCHER + [ + g_JUST, "add-to-cas", "--local-build-root", g_ROOT, + "--resolve-special=%s" % resolve_special_arg, srcdir + ], + cwd=workdir, + stdout=subprocess.PIPE, + fail_context=fail_context)[0].decode('utf-8').strip() + # Stage the resolved tree + resolved_dir: str = os.path.join(workdir, "resolved") + subdir_args: List[str] = [] + if subdir is not None: + subdir_args = ["-P", subdir] + run_cmd(g_LAUNCHER + [ + g_JUST, "install-cas", "--local-build-root", g_ROOT, + "%s::t" % resolved_tree, "-o", resolved_dir + ] + subdir_args, + cwd=workdir, + fail_context=fail_context) + move_from_dir = resolved_dir + else: + # Subdir is not None, so keep only that subdirectory + move_from_dir = os.path.join(move_from_dir, cast(str, subdir)) + + # Do the move into clone directory + os.makedirs(clone_to, exist_ok=True) + for entry in os.listdir(move_from_dir): + # shutil.move uses os.rename if on same filesystem or + # shutil.copy2 otherwise, which preserves file metadata + # and uses hardlinks if supported by the OS + try: + shutil.move(os.path.join(move_from_dir, entry), clone_to) + except Exception as ex: + fail(fail_context + "Moving file path %s failed with:\n%r" % + (os.path.join(move_from_dir, entry), ex)) + + # Clean up tmp dir + try_rmtree(workdir) + + return content + def follow_binding(repos: Json, *, repo_name: str, dep_name: str, fail_context: str) -> str: """Follow a named binding.""" @@ -2136,11 +2232,11 @@ def clone_repo(repos: Json, known_repo: str, deps_chain: List[str], report("\tCloned Git commit %s to %s" % (commit, clone_to)) elif repo_type in ["archive", "zip"]: - #TODO(psarbu): Implement archives cloning - warn(fail_context + - "Cloning from \"%s\" repositories not yet implemented!" % - (repo_type, )) - result = None + content = process_archive(repository, + repo_type, + clone_to=clone_to, + fail_context=fail_context) + report("\tCloned archive-like content %s to %s" % (content, clone_to)) elif repo_type == "foreign file": #TODO(psarbu): Implement foreign file cloning |