From 33deb69783f45d1f9f3cf5d574448f8d2f5da9e4 Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Fri, 21 Nov 2025 08:12:39 +0000 Subject: [PATCH] project dirs now called 'authored' and 'loadable' --- developer/bash/git-tar | 319 +++++++++++++++++++++++++---- developer/make/environment_RT_1.mk | 4 +- 2 files changed, 282 insertions(+), 41 deletions(-) diff --git a/developer/bash/git-tar b/developer/bash/git-tar index ec2b6eb..79d5a8c 100755 --- a/developer/bash/git-tar +++ b/developer/bash/git-tar @@ -1,39 +1,280 @@ -#!/usr/bin/env bash -# puts a tar file of the repo in the top level scratchdir, filename suffixed with UTC stamp from Z - -set -euo pipefail - -# 1) ensure we're in a git repo -git rev-parse --is-inside-work-tree >/dev/null 2>&1 || { - echo "Error: not inside a git repository." >&2 - exit 1 -} - -repo_top="$(git rev-parse --show-toplevel)" -repo_name="$(basename "$repo_top")" -ref_label="$(git describe --tags --always --dirty 2>/dev/null || git rev-parse --short HEAD)" - -# 2) ensure Z is available (prefer PATH; otherwise add repo-local RT-project-share path) -if ! command -v Z >/dev/null 2>&1; then - z_guess="${repo_top}/tool_shared/third_party/RT-project-share/release/bash" - if [ -x "${z_guess}/Z" ]; then - PATH="${z_guess}:${PATH}" - else - echo "Error: required program 'Z' not found in PATH." >&2 - echo "Hint: expected at '${z_guess}/Z' or ensure your env_* is sourced." >&2 - exit 1 - fi -fi - -# 3) timestamp and output path -stamp="$(Z)" -# trim trailing newline just in case -stamp="${stamp//$'\n'/}" - -mkdir -p "${repo_top}/scratchpad" -out="${repo_top}/scratchpad/${repo_name}__${ref_label}__${stamp}.tar.gz" - -# 4) create archive of HEAD (tracked files only; .gitignore’d stuff omitted) -git -C "$repo_top" archive --format=tar --prefix="${repo_name}/" HEAD | gzip > "$out" - -echo "Wrote ${out}" +#!/usr/bin/env python3 +# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*- + +""" +git-tar — Create an archive of the current Git repo's ref into ./scratchpad + +Commands (order-insensitive): + git-tar # default: tar.gz (HEAD, ./scratchpad, Z-stamp if importable) + git-tar help # show help + git-tar version # show version + git-tar ref- # choose ref (tag/branch/commit), default HEAD + git-tar out- # choose output directory (default: /scratchpad) + git-tar no-stamp # force omit timestamp even if Z is importable + git-tar z-format- # override timestamp format used with Z (optional) + git-tar zip # write .zip instead of .tar.gz + git-tar tar # force .tar.gz explicitly + +Output names: + __[__].tar.gz + __[__].zip +""" + +from __future__ import annotations +import gzip, os, pathlib, subprocess, sys +from typing import Optional +import importlib, importlib.util +from importlib.machinery import SourceFileLoader + +VERSION = "1.5" + +# ---------------------------------------------------------------------- +# Editable timestamp format (used when calling Z) +# ---------------------------------------------------------------------- +Z_FORMAT = "%year-%month-%day_%hour%minute%secondZ" + +USAGE = f"""git-tar {VERSION} + +Usage: + git-tar [commands...] + +Commands (order-insensitive): + help + version + ref- + out- + no-stamp + z-format- + zip + tar + +Examples: + git-tar + git-tar zip + git-tar ref-main out-/tmp + git-tar z-format-%year-%month-%dayT%hour:%minute:%second.%scintillaZ +""".rstrip() + +# ---------------------------------------------------------------------- +# git helpers +# ---------------------------------------------------------------------- +def _run(*args: str, check: bool = True, cwd: Optional[pathlib.Path] = None) -> subprocess.CompletedProcess[str]: + return subprocess.run( + args + ,check=check + ,cwd=(str(cwd) if cwd else None) + ,text=True + ,stdout=subprocess.PIPE + ,stderr=subprocess.PIPE + ) + +def _in_git_repo() -> bool: + try: + return _run("git","rev-parse","--is-inside-work-tree").stdout.strip().lower() == "true" + except subprocess.CalledProcessError: + return False + +def _git_top() -> pathlib.Path: + return pathlib.Path(_run("git","rev-parse","--show-toplevel").stdout.strip()) + +def _git_ref_label(repo_top: pathlib.Path, ref: str) -> str: + try: + return _run("git","-C",str(repo_top),"describe","--tags","--always","--dirty",ref).stdout.strip() + except subprocess.CalledProcessError: + return _run("git","-C",str(repo_top),"rev-parse","--short",ref).stdout.strip() + +# ---------------------------------------------------------------------- +# Z module discovery (supports extension-less file named "Z") +# ---------------------------------------------------------------------- +def _import_Z_module(repo_top: pathlib.Path) -> Optional[object]: + try: + return importlib.import_module("Z") + except Exception: + pass + + candidates: list[pathlib.Path] = [] + here = pathlib.Path(__file__).resolve().parent + candidates += [here / "Z", here / "Z.py"] + candidates += [ + repo_top / "tool_shared" / "third_party" / "RT-project-share" / "release" / "python" / "Z", + repo_top / "tool_shared" / "third_party" / "RT-project-share" / "release" / "python" / "Z.py", + repo_top / "tool_shared" / "third_party" / "RT-project-share" / "release" / "bash" / "Z", + ] + for d in (pathlib.Path(p) for p in (os.getenv("PATH") or "").split(os.pathsep) if p): + p = d / "Z" + if p.exists() and p.is_file(): + candidates.append(p) + + for path in candidates: + try: + if not path.exists() or not path.is_file(): + continue + spec = importlib.util.spec_from_loader("Z", SourceFileLoader("Z", str(path))) + if not spec or not spec.loader: + continue + mod = importlib.util.module_from_spec(spec) + spec.loader.exec_module(mod) # type: ignore[attr-defined] + if hasattr(mod,"make_timestamp") or (hasattr(mod,"get_utc_dict") and hasattr(mod,"format_timestamp")): + return mod + except Exception: + continue + return None + +# ---------------------------------------------------------------------- +# Z stamp helper (format string visible & editable above) +# ---------------------------------------------------------------------- +def make_z_stamp(zmod: object, z_format: str) -> Optional[str]: + try: + if hasattr(zmod, "make_timestamp"): + s = zmod.make_timestamp(fmt=z_format) # type: ignore[attr-defined] + return (str(s).strip().replace("\n","") or None) + if hasattr(zmod, "get_utc_dict") and hasattr(zmod, "format_timestamp"): + td = zmod.get_utc_dict() # type: ignore[attr-defined] + s = zmod.format_timestamp(td, z_format) # type: ignore[attr-defined] + return (str(s).strip().replace("\n","") or None) + except Exception: + return None + return None + +# ---------------------------------------------------------------------- +# archiving +# ---------------------------------------------------------------------- +def _stream_git_archive_tar(repo_top: pathlib.Path, prefix: str, ref: str, out_gz_path: pathlib.Path) -> None: + proc = subprocess.Popen( + ["git","-C",str(repo_top),"archive","--format=tar",f"--prefix={prefix}/",ref] + ,stdout=subprocess.PIPE + ) + try: + with gzip.open(out_gz_path,"wb") as gz: + while True: + chunk = proc.stdout.read(1024 * 1024) # 1 MiB + if not chunk: + break + gz.write(chunk) + finally: + if proc.stdout: + proc.stdout.close() + rc = proc.wait() + if rc != 0: + try: + out_gz_path.unlink(missing_ok=True) + finally: + raise subprocess.CalledProcessError(rc, proc.args) + +def _stream_git_archive_zip(repo_top: pathlib.Path, prefix: str, ref: str, out_zip_path: pathlib.Path) -> None: + # Directly stream git's zip to file; no Python zip building needed. + proc = subprocess.Popen( + ["git","-C",str(repo_top),"archive","--format=zip",f"--prefix={prefix}/",ref] + ,stdout=subprocess.PIPE + ) + try: + with open(out_zip_path, "wb") as f: + while True: + chunk = proc.stdout.read(1024 * 1024) + if not chunk: + break + f.write(chunk) + finally: + if proc.stdout: + proc.stdout.close() + rc = proc.wait() + if rc != 0: + try: + out_zip_path.unlink(missing_ok=True) + finally: + raise subprocess.CalledProcessError(rc, proc.args) + +# ---------------------------------------------------------------------- +# work function +# ---------------------------------------------------------------------- +def work( + ref: str = "HEAD" + ,outdir: Optional[pathlib.Path] = None + ,force_no_stamp: bool = False + ,z_format: Optional[str] = None + ,archive_kind: str = "tar" # "tar" or "zip" +) -> pathlib.Path: + if archive_kind not in ("tar","zip"): + raise RuntimeError("archive_kind must be 'tar' or 'zip'") + + if not _in_git_repo(): + raise RuntimeError("not inside a git repository") + + repo_top = _git_top() + repo_name = repo_top.name + ref_label = _git_ref_label(repo_top, ref) + + stamp: Optional[str] = None + if not force_no_stamp: + zmod = _import_Z_module(repo_top) + if zmod is not None: + stamp = make_z_stamp(zmod, z_format or Z_FORMAT) + + target_dir = (outdir or (repo_top / "scratchpad")) + target_dir.mkdir(parents=True, exist_ok=True) + + suffix = ".zip" if archive_kind == "zip" else ".tar.gz" + out_name = f"{repo_name}__{ref_label}{('__' + stamp) if stamp else ''}{suffix}" + out_path = target_dir / out_name + + if archive_kind == "zip": + _stream_git_archive_zip(repo_top, repo_name, ref, out_path) + else: + _stream_git_archive_tar(repo_top, repo_name, ref, out_path) + + return out_path + +# ---------------------------------------------------------------------- +# CLI with command tokens +# ---------------------------------------------------------------------- +def CLI(argv: Optional[list[str]] = None) -> int: + if argv is None: + argv = sys.argv[1:] + + # defaults + ref = "HEAD" + outdir: Optional[pathlib.Path] = None + force_no_stamp = False + z_format: Optional[str] = None + archive_kind = "tar" + + # no args → do the default action + if not argv: + try: + print(f"Wrote {work(ref=ref, outdir=outdir, force_no_stamp=force_no_stamp, z_format=z_format, archive_kind=archive_kind)}") + return 0 + except Exception as e: + print(f"git-tar: {e}", file=sys.stderr); return 1 + + # consume tokens (order-insensitive) + for arg in argv: + if arg in ("help","-h","--help"): + print(USAGE); return 0 + if arg == "version": + print(f"git-tar {VERSION}"); return 0 + if arg == "no-stamp": + force_no_stamp = True; continue + if arg == "zip": + archive_kind = "zip"; continue + if arg == "tar": + archive_kind = "tar"; continue + if arg.startswith("ref-"): + ref = arg[4:] or ref; continue + if arg.startswith("out-"): + od = arg[4:]; outdir = pathlib.Path(od).resolve() if od else None; continue + if arg.startswith("z-format-"): + z_format = arg[len("z-format-"):] or None; continue + print(f"git-tar: unknown command '{arg}'", file=sys.stderr); return 1 + + # run + try: + out_path = work(ref=ref, outdir=outdir, force_no_stamp=force_no_stamp, z_format=z_format, archive_kind=archive_kind) + except Exception as e: + print(f"git-tar: {e}", file=sys.stderr); return 1 + + print(f"Wrote {out_path}") + return 0 + +# ---------------------------------------------------------------------- +if __name__ == "__main__": + raise SystemExit(CLI()) diff --git a/developer/make/environment_RT_1.mk b/developer/make/environment_RT_1.mk index d5900f4..fd3758f 100644 --- a/developer/make/environment_RT_1.mk +++ b/developer/make/environment_RT_1.mk @@ -9,7 +9,7 @@ SHELL=/bin/bash ECHO := printf "%b\n" -C_SOURCE_DIR := cc +C_SOURCE_DIR := authored C := gcc CFLAGS := -std=gnu11 -Wall -Wextra -Wpedantic -finput-charset=UTF-8 CFLAGS += -MMD -MP @@ -23,7 +23,7 @@ LIBRARY_FILE := $(LIBRARY_DIR)/lib$(LIBRARY_NAME).a LN_FLAGS := -L$(LIBRARY_DIR) -L/lib64 -L/lib -MACHINE_DIR := scratchpad/machine +MACHINE_DIR := scratchpad/loadable KMOD_SOURCE_DIR := cc KMOD_CCFLAGS := -I $(KMOD_SOURCE_DIR) -- 2.20.1