diff options
| author | Igor Soarez <igor@soarez.org> | 2026-09-25 12:20:13 +0300 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-09-25 12:20:13 +0300 |
| commit | ed21d1270007a8f9cbaa2ca7da0fee1eefeb405f (patch) | |
| tree | 9f71c392b5c087b55304d1bb7123d0e436c0f343 /link/pi-models.py | |
| parent | 220553f1f016707a883632edb38b5e767728df97 (diff) | |
link/pi.sh: the generators move into files, and parse under bash 3.2 again
57e49dd made link/pi.sh unparseable on macOS. bash 3.2 does not skip heredoc
content when it scans for the end of a command substitution, so the three
apostrophes in a comment inside the new heredoc read as an unterminated quote
and the script stopped parsing at end of file. ./link.sh on a Mac aborted
partway: settings still generated, models.json stayed the symlink the commit
meant to replace, and everything after that point never ran. Linux was fine,
and so was every check I ran -- `bash` on a Mac with Homebrew is 5.x, which
parses it happily. Reported from the machine it broke.
Rewording the comment would fix it and leave the trap armed for the next edit,
so the two python programs move into link/pi-settings.py and link/pi-models.py
instead, called by path. The text is unchanged; there is no longer a heredoc
inside $( ) to trip over, and both files now compile-check on their own.
Verified under /bin/bash 3.2: first apply replaces both symlinks, a package
removed by hand still survives a relink, fragments merge for settings and for
models, and repeat runs report unchanged.
Diffstat (limited to 'link/pi-models.py')
| -rw-r--r-- | link/pi-models.py | 66 |
1 files changed, 66 insertions, 0 deletions
diff --git a/link/pi-models.py b/link/pi-models.py new file mode 100644 index 0000000..6188c94 --- /dev/null +++ b/link/pi-models.py @@ -0,0 +1,66 @@ +"""Generate ~/.pi/agent/models.json from the repo and its fragments. + +Run by link/pi.sh. It lives in a file rather than a heredoc inside $( ): +macOS ships bash 3.2, which does not skip heredoc content when it scans +for the end of a command substitution, so an odd number of apostrophes in +here would stop the whole script parsing -- which it did, on 2026-09-25. +""" + +import glob, json, os, sys + +base_path, frag_dir, target = sys.argv[1:4] + + +def load(path): + with open(path) as f: + return json.load(f) + + +# The repo's providers, then each fragment's. A fragment may add a provider or +# replace one of the same name; any other top-level key takes the last file's +# value. Which provider came from where is reported: a fragment quietly +# shadowing a provider the repo ships would otherwise be invisible. +desired = {} +notes = [] +for path in [base_path] + sorted(glob.glob(os.path.join(frag_dir, "*.json"))): + try: + layer = load(path) + except (OSError, ValueError) as e: + print("error:cannot read %s: %s" % (path, e)) + sys.exit(0) + for key, value in layer.items(): + if key == "providers": + providers = desired.setdefault("providers", {}) + if path != base_path: + for name in value: + notes.append("%s %s by %s" % ( + name, + "replaced" if name in providers else "added", + os.path.basename(path), + )) + providers.update(value) + else: + desired[key] = value + +# A symlink is the pre-generation state: replace it, even with no fragments, so +# the live file is never the tracked one. +was_link = os.path.islink(target) +try: + current = None if was_link else load(target) +except (OSError, ValueError): + current = None + +for note in notes: + print("note:" + note) + +if current == desired: + print("unchanged:") +elif os.environ.get("DRY_RUN"): + print("would-write:" + ("replacing the link" if was_link else "merged changes")) +else: + tmp = target + ".dotfiles-tmp" + with open(tmp, "w") as f: + json.dump(desired, f, indent=2) + f.write("\n") + os.replace(tmp, target) + print("wrote:" + ("replaced the link" if was_link else "merged changes")) |
