library-manager: restore lost FBD thermal-resistance change; changelog now cumulative
parent
b69a638875
commit
8781deb852
27
SKILL.md
27
SKILL.md
|
|
@ -85,6 +85,22 @@ nothing.
|
||||||
|
|
||||||
Run these in order. Each `python`/`bash` command is a helper in `scripts/`.
|
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
|
### 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,
|
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
|
**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 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 changelog lives in the **skill repo in Gitea** as well, and it is **cumulative from the
|
||||||
the new local rows are **merged onto the changelog already in Gitea** — appended, never
|
first change onward**. Two things keep it that way: at the start of a run `pull-skill` (step 0)
|
||||||
overwritten — so the Gitea copy is the growing, authoritative history across machines and
|
brings the current changelog down so a new change appends to the full history, and on push
|
||||||
sessions, and the merged file is copied back locally so the two stay in sync.
|
`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
|
## Backfilling existing parts
|
||||||
|
|
||||||
|
|
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -156,8 +156,8 @@
|
||||||
"template_version": 1
|
"template_version": 1
|
||||||
},
|
},
|
||||||
"FBD": {
|
"FBD": {
|
||||||
"skill_version": 1,
|
"skill_version": 2,
|
||||||
"template_version": 1
|
"template_version": 2
|
||||||
},
|
},
|
||||||
"FFC": {
|
"FFC": {
|
||||||
"skill_version": 1,
|
"skill_version": 1,
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
# Use a token scoped to these repos (repository: write) and rotate periodically.
|
# Use a token scoped to these repos (repository: write) and rotate periodically.
|
||||||
GIT_HOST=gitea.vecmocon.com
|
GIT_HOST=gitea.vecmocon.com
|
||||||
GIT_USER=nitishKumar
|
GIT_USER=nitishKumar
|
||||||
GIT_TOKEN=451bff1dc32202cbc0a371f8e5645079466d2120
|
GIT_TOKEN=
|
||||||
# Target repos — TWO now (set these to the exact repo names on your Gitea):
|
# 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, ...).
|
# SKILL_REPO : holds this skill's own files (SKILL.md, scripts, assets, ...).
|
||||||
# LIBRARY_REPO : holds components, one folder per Class (Diode, IC, ...); inside each,
|
# LIBRARY_REPO : holds components, one folder per Class (Diode, IC, ...); inside each,
|
||||||
|
|
|
||||||
|
|
@ -43,10 +43,18 @@ nothing is written.
|
||||||
"""
|
"""
|
||||||
import argparse, os, re, shutil, subprocess, sys, tempfile
|
import argparse, os, re, shutil, subprocess, sys, tempfile
|
||||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
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")
|
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
|
# ------------------------------------------------------------------ gitea plumbing
|
||||||
|
|
||||||
|
|
@ -199,8 +207,6 @@ def cmd_push_part(env, args):
|
||||||
|
|
||||||
# ------------------------------------------------------------------ skill repo + changelog
|
# ------------------------------------------------------------------ skill repo + changelog
|
||||||
|
|
||||||
CHANGELOG_REL = os.path.join("assets", "CHANGELOG.xlsx")
|
|
||||||
|
|
||||||
|
|
||||||
def _changelog_rows(path):
|
def _changelog_rows(path):
|
||||||
"""(workbook, sheet, [row-tuples]) for a changelog xlsx; ([]) if the file is absent."""
|
"""(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):
|
def merge_changelog(local_path, repo_path):
|
||||||
"""Append the local changelog's rows onto the repo's existing changelog (union, order
|
"""Union the local changelog's rows into the repo's changelog and keep the result sorted by
|
||||||
preserved: repo history first, then any local rows not already there). This is what keeps
|
date (chronological, oldest first). This keeps Gitea's changelog as the growing source of
|
||||||
Gitea's changelog as the growing source of truth instead of being overwritten. Writes the
|
truth — appended, never overwritten — and always in order regardless of which machine
|
||||||
merged file to repo_path and returns how many new rows were added."""
|
pushed when. Writes the merged file to repo_path and returns how many new rows were added."""
|
||||||
import shutil
|
import shutil
|
||||||
|
from openpyxl.styles import Border, Side, Alignment
|
||||||
if not os.path.exists(local_path):
|
if not os.path.exists(local_path):
|
||||||
return 0
|
return 0
|
||||||
if not os.path.exists(repo_path): # repo has none yet -> seed from local
|
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)
|
repo_wb, repo_ws, repo_rows = _changelog_rows(repo_path)
|
||||||
_, _, local_rows = _changelog_rows(local_path)
|
_, _, local_rows = _changelog_rows(local_path)
|
||||||
seen = set(repo_rows)
|
seen = set(repo_rows)
|
||||||
|
merged = list(repo_rows)
|
||||||
added = 0
|
added = 0
|
||||||
for row in local_rows:
|
for row in local_rows:
|
||||||
if row not in seen:
|
if row not in seen:
|
||||||
repo_ws.append(list(row))
|
merged.append(row)
|
||||||
seen.add(row)
|
seen.add(row)
|
||||||
added += 1
|
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)
|
repo_wb.save(repo_path)
|
||||||
return added
|
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")
|
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():
|
def main():
|
||||||
ap = argparse.ArgumentParser()
|
ap = argparse.ArgumentParser()
|
||||||
ap.add_argument("--config", default=CFG_DEFAULT)
|
ap.add_argument("--config", default=CFG_DEFAULT)
|
||||||
|
|
@ -308,10 +348,14 @@ def main():
|
||||||
help="skill directory to push (defaults to this skill)")
|
help="skill directory to push (defaults to this skill)")
|
||||||
p.add_argument("--message")
|
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()
|
args = ap.parse_args()
|
||||||
env = load_env(args.config)
|
env = load_env(args.config)
|
||||||
needs = {"GIT_HOST", "GIT_TOKEN"}
|
needs = {"GIT_HOST", "GIT_TOKEN"}
|
||||||
if args.cmd == "push-skill":
|
if args.cmd in ("push-skill", "pull-skill"):
|
||||||
needs.add("SKILL_REPO")
|
needs.add("SKILL_REPO")
|
||||||
elif args.cmd in ("check-mpn", "checkout", "commit-push", "push-part") or \
|
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)):
|
(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")
|
sys.exit(f"missing {k} in env/config")
|
||||||
{"check-mpn": cmd_check_mpn, "checkout": cmd_checkout, "list-type": cmd_list_type,
|
{"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,
|
"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__":
|
if __name__ == "__main__":
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue