From 55977dfa8d709818b3528433b2f14d5ba0fe854f Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Tue, 13 Oct 2026 04:22:10 +0000 Subject: [PATCH] . --- Harmony_path-and-staging_patch | 234 --------------------------------- 1 file changed, 234 deletions(-) delete mode 100755 Harmony_path-and-staging_patch diff --git a/Harmony_path-and-staging_patch b/Harmony_path-and-staging_patch deleted file mode 100755 index 52cb66a..0000000 --- a/Harmony_path-and-staging_patch +++ /dev/null @@ -1,234 +0,0 @@ -#!/usr/bin/env python3 -# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*- - -""" -Harmony_path-and-staging_patch - -One-time patch for the Harmony skeleton. Run it from the top directory of -the project, after checkpointing with git. - - 1. shared/tool/setup - Sources the project dependency .init files first, then adds shared/tool, - shared/made, shared/authored, so the search order becomes - shared/authored, shared/made, shared/tool, project dependencies. - 2. setup - Adds the role's tool directory after the shared setup has run, so it is - searched before everything else. Removes the earlier PATH injection, - which put it behind the shared directories. - 3. developer/tool/build_component/copy - Stages into scratchpad/made/, like the make component. - 4. developer/tool/promote - Copies from scratchpad/made into consumer/, so build intermediates - such as scratchpad/build/object stay on the scratchpad. Its setup hint - now says ./setup developer. - -Every expected piece of text is checked before any file is written. If one -is missing, the patch reports it and changes nothing. - -Usage: Harmony_path-and-staging_patch - help | -h | --help print this message (also printed when given no arguments) - preview print what would change, change nothing - apply make the changes -""" - -import sys ,re -from pathlib import Path - -EDITS = { - "shared/tool/setup": [ - ( -'''# -------------------------------------------------------------------------------- -# PATH & Plugin Sourcing -# precedence: last defined, first discovered - - PATH="$REPO_HOME/shared/authored:$PATH" - PATH="$REPO_HOME/shared/made:$PATH" - PATH="$REPO_HOME/shared/tool:$PATH" - - # Remove duplicates - clean_path() { - PATH=$(echo ":$PATH" | awk -v RS=: -v ORS=: '!seen[$0]++' | sed 's/^://; s/:$//') - } - clean_path - - # Plugin Sourcing: Dynamically load all the linked project configurations - for init_file in "$REPO_HOME/shared/project-dependency/"*.init; do - if [[ -f "$init_file" ]]; then - source "$init_file" - fi - done -''' - , -'''# -------------------------------------------------------------------------------- -# PATH & Plugin Sourcing -# Each addition goes to the front, so the last added is searched first. -# Resulting search order, most specific first: -# shared/authored, shared/made, shared/tool, project dependencies, the rest. -# The top-level setup then puts the role's tool directory in front of these. - - # Project dependencies: source each .init file. Each prepends its own entries. - for init_file in "$REPO_HOME/shared/project-dependency/"*.init; do - if [[ -f "$init_file" ]]; then - source "$init_file" - fi - done - - PATH="$REPO_HOME/shared/tool:$PATH" - PATH="$REPO_HOME/shared/made:$PATH" - PATH="$REPO_HOME/shared/authored:$PATH" - - # Remove duplicates, keeping the first occurrence - clean_path() { - PATH=$(echo ":$PATH" | awk -v RS=: -v ORS=: '!seen[$0]++' | sed 's/^://; s/:$//') - } - clean_path -''' - ) - ] - ,"setup": [ - ( -''' # Python-domain logic: PATH injection - path_parts = env.get("PATH", "").split(os.pathsep) - if tool_dir not in path_parts: - env["PATH"] = f"{tool_dir}{os.pathsep}{env.get('PATH', '')}" - -''' - , -'' - ) - ,( -""" 'if [[ -f "shared/authored/setup" ]]; then source "shared/authored/setup"; fi', -""" - , -""" 'if [[ -f "shared/authored/setup" ]]; then source "shared/authored/setup"; fi', - '# the role tool directory is searched before everything the shared setup added', - f'PATH="{tool_dir}:$PATH"; export PATH', -""" - ) - ] - ,"developer/tool/build_component/copy": [ - ('all Stages files from authored to scratchpad.' - ,'all Stages files from authored to scratchpad/made/.') - ,('clean Removes the specified namespace from scratchpad.' - ,'clean Removes scratchpad/made/.') - ,(''' src_root = os.path.join("authored", namespace_dir) - dst_root = os.path.join("scratchpad", namespace)''' - ,''' src_root = os.path.join("authored", namespace_dir) - dst_root = os.path.join("scratchpad", "made", namespace)''') - ,(''' dst_root = os.path.join("scratchpad", namespace) - if os.path.exists(dst_root):''' - ,''' dst_root = os.path.join("scratchpad", "made", namespace) - if os.path.exists(dst_root):''') - ] - ,"developer/tool/promote": [ - ('write Writes promoted files from scratchpad into consumer. Only updates newer files.' - ,'write Copies what is staged in scratchpad/made into consumer. Only updates newer files.') - ,('diff List missing, orphaned, or out of sync files between scratchpad and consumer.' - ,'diff List missing, orphaned, or out of sync files between scratchpad/made and consumer.') - ,('"Enter the project with: . setup developer\\n"' - ,'"Enter the developer role with: ./setup developer\\n"') - ,(''' ensure_dir(dst_root, DEFAULT_DIR_MODE, dry=dry) - src_root = dpath("scratchpad")''' - ,''' ensure_dir(dst_root, DEFAULT_DIR_MODE, dry=dry) - src_root = dpath("scratchpad", "made")''') - ,(''' dst_root = cpath() - src_root = dpath("scratchpad") - - src_files = set()''' - ,''' dst_root = cpath() - src_root = dpath("scratchpad", "made") - - src_files = set()''') - ,('Orphaned in consumer (missing in scratchpad)' - ,'Orphaned in consumer (missing in scratchpad/made)') - ,('newer in scratchpad)' - ,'newer in scratchpad/made)') - ,('Consumer matches developer scratchpad.' - ,'Consumer matches developer scratchpad/made.') - ] -} - -def print_help() -> int: - print(__doc__.strip()) - return 0 - -def tolerant(old: str): - """A pattern for old that ignores spaces and tabs at the ends of lines.""" - lines = old.split("\n") - last = len(lines) - 1 - return re.compile("\n".join( - re.escape(l.rstrip(" \t")) + ("" if i == last and l == "" else "[ \t]*") - for i ,l in enumerate(lines) - )) - -def plan(root: Path): - """Returns (new_texts ,already ,problems). new_texts maps a path to its new - text; already lists the files that are already patched.""" - new_texts = {} - already = [] - problems = [] - for rel ,edits in EDITS.items(): - f = root / rel - if not f.is_file(): - problems.append(f"{rel}: file not found") - continue - text = f.read_text(encoding="utf-8") - applied = 0 - for n ,(old ,new) in enumerate(edits ,1): - # The finished form is checked first: an edit's old text can lie - # inside its new text, and would otherwise be applied twice. - if new != "" and len(tolerant(new).findall(text)) == 1: - applied += 1 - continue - count = len(tolerant(old).findall(text)) - if count == 1: - text = tolerant(old).sub(lambda m: new ,text) - continue - if new == "" and count == 0: - applied += 1 - continue - problems.append(f"{rel}: edit {n}: expected text found {count} times, not once") - if applied == len(edits): - already.append(rel) - elif applied: - problems.append(f"{rel}: partly patched ({applied} of {len(edits)} edits already present)") - else: - new_texts[rel] = text - return new_texts ,already ,problems - -def work(root: Path ,preview: bool) -> int: - print(f"Harmony_path-and-staging_patch {'preview' if preview else 'apply'} in {root}") - new_texts ,already ,problems = plan(root) - if problems: - for p in problems: print(f" error {p}") - print("Nothing was changed.") - return 1 - for rel in already: - print(f" already patched {rel}") - for rel ,text in new_texts.items(): - f = root / rel - if preview: - print(f" would change {rel} ({len(EDITS[rel])} edits)") - else: - mode = f.stat().st_mode - f.write_text(text ,encoding="utf-8") - f.chmod(mode) - print(f" changed {rel} ({len(EDITS[rel])} edits)") - if preview: print("preview only: nothing was changed") - return 0 - -def CLI(argv=None) -> int: - if argv is None: argv = sys.argv[1:] - if not argv or argv[0] in ("help" ,"-h" ,"--help"): return print_help() - if len(argv) != 1 or argv[0] not in ("preview" ,"apply"): - print(f"Error: unknown command '{' '.join(argv)}'.\n" ,file=sys.stderr) - print_help() - return 1 - root = Path.cwd() - if not (root / "shared" / "tool" / "setup").is_file(): - print("Error: run this from the top directory of the project." ,file=sys.stderr) - return 1 - return work(root ,argv[0] == "preview") - -if __name__ == "__main__": - sys.exit(CLI()) -- 2.20.1