--- /dev/null
+#!/usr/bin/env -S python3 -B
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+
+import os, sys, shutil, stat, pwd, grp, glob, tempfile
+
+HELP = """usage: release {write|clean|ls|help|dry write} [DIR]
+ write [DIR] Writes released files into $REPO_HOME/release. If [DIR] is specified, only writes files found in scratchpad/DIR.
+ clean [DIR] Remove the contents of the release directories. If [DIR] is specified, clean only the contents of that release directory.
+ ls List release/ as an indented tree: PERMS OWNER NAME (root-level dotfiles printed first).
+ help Show this message.
+ dry write [DIR]
+ Preview what write would do without modifying the filesystem.
+"""
+
+ENV_MUST_BE = "developer/tool/env"
+DEFAULT_DIR_MODE = 0o700 # 077-congruent dirs
+
+def exit_with_status(msg, code=1):
+ print(f"release: {msg}", file=sys.stderr)
+ sys.exit(code)
+
+def assert_env():
+ env = os.environ.get("ENV", "")
+ if env != ENV_MUST_BE:
+ hint = (
+ "ENV is not 'developer/tool/env'.\n"
+ "Enter the project with: source ./env_developer\n"
+ "That script exports: ROLE=developer; ENV=$ROLE/tool/env"
+ )
+ exit_with_status(f"bad environment: ENV='{env}'. {hint}")
+
+def repo_home():
+ rh = os.environ.get("REPO_HOME")
+ if not rh:
+ exit_with_status("REPO_HOME not set (did you 'source ./env_developer'?)")
+ return rh
+
+def dpath(*parts):
+ return os.path.join(repo_home(), "developer", *parts)
+
+def rpath(*parts):
+ return os.path.join(repo_home(), "release", *parts)
+
+def dev_root():
+ return dpath()
+
+def rel_root():
+ return rpath()
+
+def _display_src(p_abs: str) -> str:
+ try:
+ if os.path.commonpath([dev_root()]) == os.path.commonpath([dev_root(), p_abs]):
+ return os.path.relpath(p_abs, dev_root())
+ except Exception:
+ pass
+ return p_abs
+
+def _display_dst(p_abs: str) -> str:
+ try:
+ rel = os.path.relpath(p_abs, rel_root())
+ rel = "" if rel == "." else rel
+ return "$REPO_HOME/release" + ("/" + rel if rel else "")
+ except Exception:
+ return p_abs
+
+def ensure_mode(path, mode):
+ try: os.chmod(path, mode)
+ except Exception: pass
+
+def ensure_dir(path, mode=DEFAULT_DIR_MODE, dry=False):
+ if dry:
+ if not os.path.isdir(path):
+ shown = _display_dst(path) if path.startswith(rel_root()) else (
+ os.path.relpath(path, dev_root()) if path.startswith(dev_root()) else path
+ )
+ print(f"(dry) mkdir -m {oct(mode)[2:]} '{shown}'")
+ return
+ os.makedirs(path, exist_ok=True)
+ ensure_mode(path, mode)
+
+def filemode(m):
+ try: return stat.filemode(m)
+ except Exception: return oct(m & 0o777)
+
+def owner_group(st):
+ try: return f"{pwd.getpwuid(st.st_uid).pw_name}:{grp.getgrgid(st.st_gid).gr_name}"
+ except Exception: return f"{st.st_uid}:{st.st_gid}"
+
+# ---------- LS (two-pass owner:group width) ----------
+def list_tree(root):
+ if not os.path.isdir(root):
+ return
+ entries = []
+ def gather(path: str, depth: int, is_root: bool):
+ try:
+ it = list(os.scandir(path))
+ except FileNotFoundError:
+ return
+ dirs = [e for e in it if e.is_dir(follow_symlinks=False)]
+ files = [e for e in it if not e.is_dir(follow_symlinks=False)]
+ dirs.sort(key=lambda e: e.name); files.sort(key=lambda e: e.name)
+
+ if is_root:
+ for f in (e for e in files if e.name.startswith(".")):
+ st = os.lstat(f.path); entries.append((False, depth, filemode(st.st_mode), owner_group(st), f.name))
+ for d in dirs:
+ st = os.lstat(d.path); entries.append((True, depth, filemode(st.st_mode), owner_group(st), d.name + "/"))
+ gather(d.path, depth + 1, False)
+ for f in (e for e in files if not e.name.startswith(".")):
+ st = os.lstat(f.path); entries.append((False, depth, filemode(st.st_mode), owner_group(st), f.name))
+ else:
+ for d in dirs:
+ st = os.lstat(d.path); entries.append((True, depth, filemode(st.st_mode), owner_group(st), d.name + "/"))
+ gather(d.path, depth + 1, False)
+ for f in files:
+ st = os.lstat(f.path); entries.append((False, depth, filemode(st.st_mode), owner_group(st), f.name))
+ gather(root, depth=1, is_root=True)
+
+ ogw = 0
+ for (_isdir, _depth, _perms, ownergrp, _name) in entries:
+ if len(ownergrp) > ogw: ogw = len(ownergrp)
+
+ print("release/")
+ for (_isdir, depth, perms, ownergrp, name) in entries:
+ indent = " " * depth
+ print(f"{perms} {ownergrp:<{ogw}} {indent}{name}")
+# ---------- end LS ----------
+
+def iter_src_files(topdir, src_root):
+ base = os.path.join(src_root, topdir) if topdir else src_root
+ if not os.path.isdir(base):
+ return
+ yield
+ if topdir == "kmod":
+ for p in sorted(glob.glob(os.path.join(base, "*.ko"))):
+ yield (p, os.path.basename(p))
+ else:
+ for root, dirs, files in os.walk(base):
+ dirs.sort(); files.sort()
+ for fn in files:
+ src = os.path.join(root, fn)
+ rel = os.path.relpath(src, base)
+ yield (src, rel)
+
+def _target_mode_from_source(src_abs: str) -> int:
+ """077 policy: files 0600; if source has owner-exec, make 0700."""
+ try:
+ sm = stat.S_IMODE(os.stat(src_abs).st_mode)
+ except FileNotFoundError:
+ return 0o600
+ return 0o700 if (sm & stat.S_IXUSR) else 0o600
+
+def copy_one(src_abs, dst_abs, dry=False):
+ src_show = _display_src(src_abs)
+ dst_show = _display_dst(dst_abs)
+ parent = os.path.dirname(dst_abs)
+ os.makedirs(parent, exist_ok=True)
+ target_mode = _target_mode_from_source(src_abs)
+
+ def _is_writable_dir(p): return os.access(p, os.W_OK)
+ flip_needed = not _is_writable_dir(parent)
+ restore_mode = None
+ parent_show = _display_dst(parent)
+
+ if dry:
+ if flip_needed:
+ print(f"(dry) chmod u+w '{parent_show}'")
+ if os.path.exists(dst_abs):
+ print(f"(dry) unlink '{dst_show}'")
+ # show final mode we will set
+ print(f"(dry) install -m {oct(target_mode)[2:]} -D '{src_show}' '{dst_show}'")
+ if flip_needed:
+ print(f"(dry) chmod u-w '{parent_show}'")
+ return
+
+ try:
+ if flip_needed:
+ try:
+ st_parent = os.stat(parent)
+ restore_mode = stat.S_IMODE(st_parent.st_mode)
+ os.chmod(parent, restore_mode | stat.S_IWUSR)
+ except PermissionError:
+ exit_with_status(f"cannot write: parent dir not writable and chmod failed on {parent_show}")
+
+ # Atomic replace with enforced 077-compliant mode
+ fd, tmp_path = tempfile.mkstemp(prefix='.tmp.', dir=parent)
+ try:
+ with os.fdopen(fd, "wb") as tmpf, open(src_abs, "rb") as sf:
+ shutil.copyfileobj(sf, tmpf)
+ tmpf.flush()
+ os.chmod(tmp_path, target_mode)
+ os.replace(tmp_path, dst_abs)
+ finally:
+ try:
+ if os.path.exists(tmp_path):
+ os.unlink(tmp_path)
+ except Exception:
+ pass
+ finally:
+ if restore_mode is not None:
+ try: os.chmod(parent, restore_mode)
+ except Exception: pass
+
+ print(f"+ install -m {oct(target_mode)[2:]} '{src_show}' '{dst_show}'")
+
+def write_one_dir(topdir, dry):
+ rel_root_dir = rpath()
+ src_root = dpath("scratchpad")
+ src_dir = os.path.join(src_root, topdir)
+ dst_dir = os.path.join(rel_root_dir, topdir)
+
+ if not os.path.isdir(src_dir):
+ exit_with_status(
+ f"cannot write: expected '{_display_src(src_dir)}' to exist. "
+ f"Create scratchpad/{topdir} (Makefiles may need to populate it)."
+ )
+
+ ensure_dir(dst_dir, DEFAULT_DIR_MODE, dry=dry)
+
+ wrote = False
+ for src_abs, rel in iter_src_files(topdir, src_root):
+ dst_abs = os.path.join(dst_dir, rel)
+ copy_one(src_abs, dst_abs, dry=dry)
+ wrote = True
+ if not wrote:
+ msg = "no matching artifacts found"
+ if topdir == "kmod": msg += " (looking for *.ko)"
+ print(f"(info) {msg} in {_display_src(src_dir)}")
+
+def cmd_write(dir_arg, dry=False):
+ assert_env()
+ ensure_dir(rpath(), DEFAULT_DIR_MODE, dry=dry)
+
+ src_root = dpath("scratchpad")
+ if not os.path.isdir(src_root):
+ exit_with_status(f"cannot find developer scratchpad at '{_display_src(src_root)}'")
+
+ if dir_arg:
+ write_one_dir(dir_arg, dry=dry)
+ else:
+ subs = sorted([e.name for e in os.scandir(src_root) if e.is_dir(follow_symlinks=False)])
+ if not subs:
+ print(f"(info) nothing to release; no subdirectories found under {_display_src(src_root)}")
+ return
+ for td in subs:
+ write_one_dir(td, dry=dry)
+
+def _clean_contents(dir_path):
+ if not os.path.isdir(dir_path): return
+ for name in os.listdir(dir_path):
+ p = os.path.join(dir_path, name)
+ if os.path.isdir(p) and not os.path.islink(p):
+ shutil.rmtree(p, ignore_errors=True)
+ else:
+ try: os.unlink(p)
+ except FileNotFoundError: pass
+
+def cmd_clean(dir_arg):
+ assert_env()
+ rel_root_dir = rpath()
+ if not os.path.isdir(rel_root_dir):
+ return
+ if dir_arg:
+ _clean_contents(os.path.join(rel_root_dir, dir_arg))
+ else:
+ for e in os.scandir(rel_root_dir):
+ if e.is_dir(follow_symlinks=False):
+ _clean_contents(e.path)
+
+def CLI():
+ if len(sys.argv) < 2:
+ print(HELP); return
+ cmd, *args = sys.argv[1:]
+ if cmd == "write":
+ cmd_write(args[0] if args else None, dry=False)
+ elif cmd == "clean":
+ cmd_clean(args[0] if args else None)
+ elif cmd == "ls":
+ list_tree(rpath())
+ elif cmd == "help":
+ print(HELP)
+ elif cmd == "dry":
+ if args and args[0] == "write":
+ cmd_write(args[1] if len(args) >= 2 else None, dry=True)
+ else:
+ print(HELP)
+ else:
+ print(HELP)
+
+if __name__ == "__main__":
+ CLI()
--- /dev/null
+#!/usr/bin/env -S python3 -B
+# -*- mode: python; coding: utf-8; python-indent-offset: 2; indent-tabs-mode: nil -*-
+
+"""
+set_project_permissions — normalize a freshly cloned project to Harmony policies.
+
+usage:
+ set_project_permissions [default]
+ set_project_permissions help | --help | -h
+
+notes:
+ • Must be run from the toolsmith environment (ENV=tool/env, ROLE=toolsmith).
+ • Starts at $REPO_HOME.
+ • Baseline is umask-077 congruence:
+ - directories → 0700
+ - files → 0600, but preserve owner-exec (→ 0700 for executables)
+ applied to the entire repo, including release/, EXCEPT:
+ - release/kmod/*.ko → 0440
+ • Skips .git/ and symlinks.
+"""
+
+import os, sys, stat
+
+# Must match tool_shared/bespoke/env policy:
+DEFAULT_UMASK = 0o077 # reminder only; effective modes below implement 077 congruence.
+
+DIR_MODE_077 = 0o700
+
+def die(msg, code=1):
+ print(f"set_project_permissions: {msg}", file=sys.stderr)
+ sys.exit(code)
+
+def require_toolsmith_env():
+ env = os.environ.get("ENV", "")
+ role = os.environ.get("ROLE", "")
+ if env != "tool/env" or role != "toolsmith":
+ hint = (
+ "This script should be run from the toolsmith environment.\n"
+ "Try: source ./env_toolsmith (then re-run: set_project_permissions default)"
+ )
+ die(f"bad environment: ENV='{env}' ROLE='{role}'.\n{hint}")
+
+def repo_home():
+ rh = os.environ.get("REPO_HOME")
+ if not rh:
+ die("REPO_HOME is not set (did you source tool_shared/bespoke/env?)")
+ return os.path.realpath(rh)
+
+def show_path(p, rh):
+ return p.replace(rh, "$REPO_HOME", 1) if p.startswith(rh) else p
+
+def is_git_dir(path):
+ return os.path.basename(path.rstrip(os.sep)) == ".git"
+
+def file_target_mode_077_preserve_exec(current_mode: int) -> int:
+ # Base 0600, add owner exec if currently set; drop all group/other.
+ target = 0o600
+ if current_mode & stat.S_IXUSR:
+ target |= stat.S_IXUSR
+ return target
+
+def set_mode_if_needed(path, target, rh):
+ try:
+ st = os.lstat(path)
+ except FileNotFoundError:
+ return 0
+ cur = stat.S_IMODE(st.st_mode)
+ if cur == target:
+ return 0
+ os.chmod(path, target)
+ print(f"+ chmod {oct(target)[2:]} '{show_path(path, rh)}'")
+ return 1
+
+def apply_policy(rh):
+ changed = 0
+ release_root = os.path.join(rh, "release")
+ for dirpath, dirnames, filenames in os.walk(rh, topdown=True, followlinks=False):
+ # prune .git
+ dirnames[:] = [d for d in dirnames if d != ".git"]
+
+ # directories: 0700 everywhere (incl. release/)
+ changed += set_mode_if_needed(dirpath, DIR_MODE_077, rh)
+
+ # files: 0600 (+owner exec) everywhere, except release/kmod/*.ko → 0440
+ rel_from_repo = os.path.relpath(dirpath, rh)
+ under_release = rel_from_repo == "release" or rel_from_repo.startswith("release"+os.sep)
+ top_under_release = ""
+ if under_release:
+ rel_from_release = os.path.relpath(dirpath, release_root)
+ top_under_release = (rel_from_release.split(os.sep, 1)[0] if rel_from_release != "." else "")
+
+ for fn in filenames:
+ p = os.path.join(dirpath, fn)
+ if os.path.islink(p):
+ continue
+ try:
+ st = os.lstat(p)
+ except FileNotFoundError:
+ continue
+
+ if under_release and top_under_release == "kmod" and fn.endswith(".ko"):
+ target = 0o440
+ else:
+ target = file_target_mode_077_preserve_exec(stat.S_IMODE(st.st_mode))
+
+ changed += set_mode_if_needed(p, target, rh)
+ return changed
+
+def cmd_default():
+ require_toolsmith_env()
+ rh = repo_home()
+ total = apply_policy(rh)
+ print(f"changes: {total}")
+
+def main():
+ if len(sys.argv) == 1 or sys.argv[1] in ("default",):
+ return cmd_default()
+ if sys.argv[1] in ("help", "--help", "-h"):
+ print(__doc__.strip()); return 0
+ # unknown command → help
+ print(__doc__.strip()); return 1
+
+if __name__ == "__main__":
+ sys.exit(main())