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 9392925..facf0d2 100644 Binary files a/assets/CHANGELOG.xlsx and b/assets/CHANGELOG.xlsx differ diff --git a/assets/template/template.xlsx b/assets/template/template.xlsx index ac03036..def408c 100644 Binary files a/assets/template/template.xlsx and b/assets/template/template.xlsx differ diff --git a/assets/template/versions.json b/assets/template/versions.json index f470c73..86f7311 100644 --- a/assets/template/versions.json +++ b/assets/template/versions.json @@ -156,8 +156,8 @@ "template_version": 1 }, "FBD": { - "skill_version": 1, - "template_version": 1 + "skill_version": 2, + "template_version": 2 }, "FFC": { "skill_version": 1, diff --git a/config/gitea.env b/config/gitea.env index c6c6bfd..c51bcf8 100644 --- a/config/gitea.env +++ b/config/gitea.env @@ -3,7 +3,7 @@ # Use a token scoped to these repos (repository: write) and rotate periodically. GIT_HOST=gitea.vecmocon.com GIT_USER=nitishKumar -GIT_TOKEN=451bff1dc32202cbc0a371f8e5645079466d2120 +GIT_TOKEN= # Target repos — TWO now (set these to the exact repo names on your Gitea): # SKILL_REPO : holds this skill's own files (SKILL.md, scripts, assets, ...). # LIBRARY_REPO : holds components, one folder per Class (Diode, IC, ...); inside each, diff --git a/scripts/gitea_components.py b/scripts/gitea_components.py index b389ef1..39871cc 100644 --- a/scripts/gitea_components.py +++ b/scripts/gitea_components.py @@ -43,10 +43,18 @@ nothing is written. """ import argparse, os, re, shutil, subprocess, sys, tempfile sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) -from common import class_folder, load_taxonomy, mpn_make_prefix +from common import (class_folder, load_taxonomy, mpn_make_prefix, + SKILL_ROOT, TEMPLATE_XLSX, VERSIONS_JSON, CHANGELOG_XLSX) CFG_DEFAULT = os.path.join(os.path.dirname(__file__), "..", "config", "gitea.env") +# The skill's stateful files (accumulate over time) — kept in the SKILL repo and pulled at the +# start of a run so template/version/changelog changes always build on Gitea's latest state. +# Paths are derived from common so the writer (append_parameter) and the sync can never drift. +STATE_FILES = [os.path.relpath(p, SKILL_ROOT) + for p in (TEMPLATE_XLSX, VERSIONS_JSON, CHANGELOG_XLSX)] +CHANGELOG_REL = os.path.relpath(CHANGELOG_XLSX, SKILL_ROOT) + # ------------------------------------------------------------------ gitea plumbing @@ -199,8 +207,6 @@ def cmd_push_part(env, args): # ------------------------------------------------------------------ skill repo + changelog -CHANGELOG_REL = os.path.join("assets", "CHANGELOG.xlsx") - def _changelog_rows(path): """(workbook, sheet, [row-tuples]) for a changelog xlsx; ([]) if the file is absent.""" @@ -215,11 +221,12 @@ def _changelog_rows(path): def merge_changelog(local_path, repo_path): - """Append the local changelog's rows onto the repo's existing changelog (union, order - preserved: repo history first, then any local rows not already there). This is what keeps - Gitea's changelog as the growing source of truth instead of being overwritten. Writes the - merged file to repo_path and returns how many new rows were added.""" + """Union the local changelog's rows into the repo's changelog and keep the result sorted by + date (chronological, oldest first). This keeps Gitea's changelog as the growing source of + truth — appended, never overwritten — and always in order regardless of which machine + pushed when. Writes the merged file to repo_path and returns how many new rows were added.""" import shutil + from openpyxl.styles import Border, Side, Alignment if not os.path.exists(local_path): return 0 if not os.path.exists(repo_path): # repo has none yet -> 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__":