From 8781deb852344574d75d4ee12c374db3cb8967ab Mon Sep 17 00:00:00 2001 From: library-manager Date: Fri, 10 Jul 2026 11:01:28 +0000 Subject: [PATCH] library-manager: restore lost FBD thermal-resistance change; changelog now cumulative --- SKILL.md | 27 +++++++++++--- assets/CHANGELOG.xlsx | Bin 5234 -> 5314 bytes assets/template/template.xlsx | Bin 110079 -> 110099 bytes assets/template/versions.json | 4 +-- config/gitea.env | 2 +- scripts/gitea_components.py | 64 ++++++++++++++++++++++++++++------ 6 files changed, 80 insertions(+), 17 deletions(-) diff --git a/SKILL.md b/SKILL.md index 592c570..fc23394 100644 --- a/SKILL.md +++ b/SKILL.md @@ -85,6 +85,22 @@ nothing. Run these in order. Each `python`/`bash` command is a helper in `scripts/`. +### 0. Sync the skill state from Gitea first — always + +The skill's state (the template, the per-typeid versions, and the changelog) lives in the +**skill repo**, and it grows over time. A fresh install/session starts from the packaged v1 +state, so if you don't sync first, a second template change wouldn't build on the first — the +versions would restart at v1 and the changelog would look like it only holds the latest change. +**So begin every run by pulling the current state:** + +```bash +python scripts/gitea_components.py pull-skill +``` + +This copies `template.xlsx`, `versions.json`, and `CHANGELOG.xlsx` from the skill repo into the +local skill, so version bumps continue correctly (v2 → v3 → …) and the changelog stays +**cumulative from the very first change**. + ### 1. Duplicate check first — before any real work The part's presence is keyed on **MPN + make** (typeid not known yet). If it already exists, @@ -239,10 +255,13 @@ typeid's template/version changes, one row is appended with columns **Date | Typeid | Skill Version | Template Version | Description** — the version columns hold the new versions, and Description is your note (or the parameter(s) added if you gave none). -The changelog lives in the **skill repo in Gitea** as well. When you push (via `push-skill`), -the new local rows are **merged onto the changelog already in Gitea** — appended, never -overwritten — so the Gitea copy is the growing, authoritative history across machines and -sessions, and the merged file is copied back locally so the two stay in sync. +The changelog lives in the **skill repo in Gitea** as well, and it is **cumulative from the +first change onward**. Two things keep it that way: at the start of a run `pull-skill` (step 0) +brings the current changelog down so a new change appends to the full history, and on push +`push-skill` **merges** the new local rows onto the changelog already in Gitea — appended, +never overwritten. So the Gitea copy is the growing, authoritative history across machines and +sessions; the merged file is copied back locally so the two stay in sync. If you ever see the +Gitea changelog with only the latest change, it means step 0 (`pull-skill`) was skipped. ## Backfilling existing parts diff --git a/assets/CHANGELOG.xlsx b/assets/CHANGELOG.xlsx index 93929254fa904a23cbfb38cb5be514e9c0ee2e41..facf0d2a65f69d4cce4288fac193d1e75ec00750 100644 GIT binary patch delta 2022 zcmZWq2T&8(7EMA68whLwiIFB7ic5lrip zOC6rXQ58Q2C1s$aT0yzxOl?`?hhRv4B?p1m3|rz$NA#r1sn_?Q2mpWT`mj65{ReE= zEqG4oXuAZe6I|vNB+&rb@3Y-C4}Itv?Go-N+lhw8y>>kePDmmpzgXT?>s)^L!rQu8 zq^ejgFYyzB9N(6Rt(qCEnlekD+QXiU(^?+g;C;@Rv>)E!o*Qr5zpDV^OE$BDR7q0C z)3?O`Q4k=8wFE?d`g>;)u|rt{*s~tO^PwquB9_Q z-h_WLaX#yC^0f5q!%FduH<~BB?6UMIwohqCINWKPlE)>G6Y0Hx*}*FC9nga zGPCwg562;cDSx@*iKmgyr+LJx!CPmQvw36f)ss&a)1nt!+1Z25zvr`z)368(_0{aW z?TE)3W$D=+(5<-0OiK9Lt8@*w+v7T8G$ZQ-{i}uTsYwTFtj;RCR3+jj*}$dqFe8i0 z$^v2;gm)K(kn1R6qma|9NVPa=Ug+D4;Qi^$~mBD0nsKxQ=Eg!e54$J72)k@9_cdK2Sd_1t4 z(K09$XI-)OivLX`!N{vo%g70{o_z3i16SWN!M>iNNb%vD)y3W~XY*df$8*f?#RniX zd(-L#cmjb}2wrwxQ~y zs53vy0sx{7NFW-;#3mAPF@_1}h~qxf;Uy?}ic*(Y7V?3<%ijK%iuit0?=b30otUKl z?pIlh4y6>UJzzZ9+d_1KP9w)pj>j*sm@5`>GwZE)y%OzuN(?{hRgB2l_TrS7jvL1t z8v3ZkN1qq#S{CAZ8w(1ah@pEf^n@u%m;`6HO`4F&54;y}cO$%J>b_#z^!^odjhL+0 zd$0W7#81Q@7*ih2$bt{B)7P)wJE}mHEqMQ;YUsSA{x8-hgf#JB>`zZ% z3+7uGOo`il_pG&|@OS=f$`GvL0TD_HK^@zq9-YhPXLwc%t`#u2C408RY;??j-+QX4 z_z5m?z&>F7Sm^|H3}FU&w=$4?aIGxwP@!@~(_LZC+oSmAQ#gqrEkE5ZO3HqBKy~d&8kx-W9_k7QCc0t2n!!j!(7)Fr!4BF5OLIXW z^_9Ip!N+czXZ4L?2TAIMzeW3yRQ$}&gJ{<#YCfcomP|%3H?Fup7!5qrdqa>Ls7unp zvn7qyuN<B>iVW03CiVAW?QniJ6(1};DltuY zHt82OTrBit!ahaN%zda2e1JJr@4iVKRts@B;PbQ>> zi(9#SG4~R5LW*V@C9@xgr(6o%2n&q@(3l|ZVfjf_DbmYqX*-N})}E#dQLZYXhs|3hAh9-t^M84lY? F`U@xIl?4C* delta 1917 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 D8kI7Z delta 6479 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 seed from local @@ -230,12 +237,24 @@ def merge_changelog(local_path, repo_path): repo_wb, repo_ws, repo_rows = _changelog_rows(repo_path) _, _, local_rows = _changelog_rows(local_path) seen = set(repo_rows) + merged = list(repo_rows) added = 0 for row in local_rows: if row not in seen: - repo_ws.append(list(row)) + merged.append(row) seen.add(row) added += 1 + merged.sort(key=lambda r: r[0]) # chronological by Date (column A) + if repo_ws.max_row > 1: # rewrite data rows, keep the header + repo_ws.delete_rows(2, repo_ws.max_row - 1) + s = Side(style="thin", color="BFBFBF") + border = Border(left=s, right=s, top=s, bottom=s) + for row in merged: + repo_ws.append(list(row)) + rr = repo_ws.max_row + for c in range(1, 6): + repo_ws.cell(rr, c).border = border + repo_ws.cell(rr, c).alignment = Alignment(vertical="center", wrap_text=(c == 5), horizontal="left") repo_wb.save(repo_path) return added @@ -281,6 +300,27 @@ def cmd_push_skill(env, args): commit_push(tmp, env.get("GIT_TOKEN", ""), args.message or "library-manager: sync skill files + changelog") +def cmd_pull_skill(env, args): + """Pull the skill's stateful files (template.xlsx, versions.json, CHANGELOG.xlsx) from the + SKILL repo into the local skill BEFORE making changes. This is what makes template/version + changes cumulative: without it, a fresh run starts from the packaged v1 state, so a second + change wouldn't build on the first (versions would restart at v1 and the changelog would + look like it only has the latest change). Run this at the start of every run.""" + src = os.path.abspath(args.src) + tmp = tempfile.mkdtemp(prefix="skillpull_") + clone(env, tmp, env["SKILL_REPO"]) + pulled = [] + for rel in STATE_FILES: + s = os.path.join(tmp, rel) + d = os.path.join(src, rel) + if os.path.exists(s): + os.makedirs(os.path.dirname(d), exist_ok=True) + shutil.copy2(s, d) + pulled.append(rel) + print("pulled from skill repo: " + (", ".join(pulled) if pulled else + "nothing yet (skill repo has no template/version/changelog state)")) + + def main(): ap = argparse.ArgumentParser() ap.add_argument("--config", default=CFG_DEFAULT) @@ -308,10 +348,14 @@ def main(): help="skill directory to push (defaults to this skill)") p.add_argument("--message") + p = sub.add_parser("pull-skill") + p.add_argument("--src", default=os.path.join(os.path.dirname(__file__), ".."), + help="skill directory to sync into (defaults to this skill)") + args = ap.parse_args() env = load_env(args.config) needs = {"GIT_HOST", "GIT_TOKEN"} - if args.cmd == "push-skill": + if args.cmd in ("push-skill", "pull-skill"): needs.add("SKILL_REPO") elif args.cmd in ("check-mpn", "checkout", "commit-push", "push-part") or \ (args.cmd in ("list-type", "place-part") and not getattr(args, "local", None) and not getattr(args, "root", None)): @@ -321,7 +365,7 @@ def main(): sys.exit(f"missing {k} in env/config") {"check-mpn": cmd_check_mpn, "checkout": cmd_checkout, "list-type": cmd_list_type, "place-part": cmd_place_part, "commit-push": cmd_commit_push, "push-part": cmd_push_part, - "push-skill": cmd_push_skill}[args.cmd](env, args) + "push-skill": cmd_push_skill, "pull-skill": cmd_pull_skill}[args.cmd](env, args) if __name__ == "__main__":