From 58912649cd910f195fef5225e1046093ed436801 Mon Sep 17 00:00:00 2001 From: Cliff Brake Date: Thu, 3 Sep 2026 17:26:49 -0400 Subject: [PATCH 1/2] feat: accept multiple references in add and remove A release configuration could only name one reference per `remove` entry, so depopulating a board variant meant one line per part. `add` accepted several references but only when separated by commas, which left the released BOM mixing comma-separated references on added lines with space-separated ones everywhere else. That also meant a reference added by one operation could never be removed by another, since removal matched on space-separated references. Both operations now take several references in a single `ref` field, separated by spaces or commas, and added references are stored space-separated to match the rest of the BOM. Added references are also sorted, using the sortRefs helper that was already present but never called. Existing configurations keep working: a single reference and comma-separated lists are both still accepted. Claude-Session: https://claude.ai/code/session_01Y2dKhCQGfRFMwJbrvcDmxJ --- CHANGELOG.md | 6 ++++++ README.md | 12 +++++++++++- bom.go | 26 +++++++++++++++++++++----- rel-script.go | 15 ++++++++++----- rel-script_test.go | 16 +++++++++++++--- 5 files changed, 61 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e868658..9113643 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,12 @@ For more details or to discuss releases, please visit the ## [Unreleased] +- Release configurations: `add` and `remove` accept several references in one + `ref` field, separated by spaces or commas. Depopulating a board variant no + longer needs a separate `remove` line per part, and added references are + stored space-separated and sorted so they match the rest of the BOM. Single + and comma-separated references keep working as before. + ## [0.9.5] - 2026-09-03 - KiCad HTTP API: `categoryPrefixedNames: true` in `gitplm.yml` serves parts as diff --git a/README.md b/README.md index 18b2ef4..59c95c2 100644 --- a/README.md +++ b/README.md @@ -283,10 +283,14 @@ remove: - cmpName: Test point - cmpName: Test point 2 - ref: D12 + - ref: D20 D21 D22 add: - cmpName: "screw #4,2" ref: S3 ipn: SCR-002-0002 + - cmpName: "led green" + ref: D20 D21 D22 + ipn: DIO-033-000G hooks: - date -Iseconds > {{ .RelDir }}/timestamp.txt - | @@ -308,8 +312,14 @@ The following template variables are available: Supported operations: -- `remove`: remove a part from a BOM +- `remove`: remove a part from a BOM, matched by `cmpName` or `ref` - `add`: add a part to a BOM + +Both operations accept several references in one `ref` field, separated by +spaces or commas. On `add`, the quantity is taken from the number of references +and the references are sorted; omit `ref` for a part with no reference +designator, such as a sub assembly, and the quantity is 1. A BOM line is dropped +once all of its references have been removed. - `copy`: copy a file or directory to the release directory - `hooks`: run shell scripts (currently Linux and MacOS only). Can be used to build software, generate PDFs, etc. diff --git a/bom.go b/bom.go index d2ce29c..81b52a5 100644 --- a/bom.go +++ b/bom.go @@ -7,6 +7,7 @@ import ( "sort" "strconv" "strings" + "unicode" ) type bomLine struct { @@ -40,15 +41,30 @@ func (bl *bomLine) String() string { bl.Checked) } -func (bl *bomLine) removeRef(ref string) { - refs := strings.Split(bl.Ref, " ") +// splitRefs breaks a reference designator field into individual references. +// References may be separated by spaces or commas, so that a BOM and a release +// configuration can both be written in whichever style reads best. +func splitRefs(refs string) []string { + return strings.FieldsFunc(refs, func(r rune) bool { + return r == ',' || unicode.IsSpace(r) + }) +} + +// removeRefs removes one or more references from a BOM line and updates the +// quantity to match. +func (bl *bomLine) removeRefs(remove []string) { + rm := make(map[string]bool, len(remove)) + for _, r := range remove { + rm[r] = true + } + refsOut := []string{} - for _, r := range refs { - r = strings.Trim(r, " ") - if r != ref && r != "" { + for _, r := range splitRefs(bl.Ref) { + if !rm[r] { refsOut = append(refsOut, r) } } + bl.Ref = strings.Join(refsOut, " ") bl.Qty = float64(len(refsOut)) } diff --git a/rel-script.go b/rel-script.go index 744ff45..2751076 100644 --- a/rel-script.go +++ b/rel-script.go @@ -36,10 +36,10 @@ func (rs *relScript) processBom(b bom) (bom, error) { ret = retM } - if r.Ref != "" { + if refs := splitRefs(r.Ref); len(refs) > 0 { retM := bom{} for _, l := range ret { - l.removeRef(r.Ref) + l.removeRefs(refs) if l.Qty > 0 { retM = append(retM, l) } @@ -49,9 +49,14 @@ func (rs *relScript) processBom(b bom) (bom, error) { } for _, a := range rs.Add { - refs := strings.Split(a.Ref, ",") - a.Qty = float64(len(refs)) - if a.Qty < 0 { + refs := splitRefs(a.Ref) + if len(refs) > 0 { + a.Ref = strings.Join(refs, " ") + a.Qty = float64(len(refs)) + a.sortRefs() + } else { + // a part with no reference, such as a sub assembly + a.Ref = "" a.Qty = 1.0 } // for some reason we need to make a copy or it diff --git a/rel-script_test.go b/rel-script_test.go index ce2d6b0..ebd5b3c 100644 --- a/rel-script_test.go +++ b/rel-script_test.go @@ -14,12 +14,20 @@ var modFile = ` description: modify bom remove: - cmpName: Test point 2 - - ref: D13 - ref: R11 + - ref: D13 D14 + - ref: R1,R2 add: - cmpName: "screw #4 2" ref: S3 ipn: SCR-002-0002 + - cmpName: "led green" + ref: D22 D20 D21 + ipn: DIO-033-000G + - cmpName: "led blue" + ref: D32,D30,D31 + ipn: DIO-033-000B + - ipn: PCB-009-0013 ` var bomIn = ` @@ -32,8 +40,10 @@ D1 D2 D13 D14,4,,diode,,,,DIO-023-0023, var bomExp = ` Ref,Qty,Value,Cmp name,Footprint,Description,Vendor,IPN,Datasheet -D1 D2 D14,3,,diode,,,,DIO-023-0023, -R1 R2,2,,100K_100mw,,,,RES-006-0232, +D1 D2,2,,diode,,,,DIO-023-0023, +D30 D31 D32,3,,led blue,,,,DIO-033-000B, +D20 D21 D22,3,,led green,,,,DIO-033-000G, +,1,,,,,,PCB-009-0013, S3,1,,screw #4 2,,,,SCR-002-0002, ` From 9aa87663d41d7445d6042149eb8fecbe05db5a36 Mon Sep 17 00:00:00 2001 From: Cliff Brake Date: Thu, 3 Sep 2026 17:33:07 -0400 Subject: [PATCH 2/2] format --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 59c95c2..902dd99 100644 --- a/README.md +++ b/README.md @@ -320,6 +320,7 @@ spaces or commas. On `add`, the quantity is taken from the number of references and the references are sorted; omit `ref` for a part with no reference designator, such as a sub assembly, and the quantity is 1. A BOM line is dropped once all of its references have been removed. + - `copy`: copy a file or directory to the release directory - `hooks`: run shell scripts (currently Linux and MacOS only). Can be used to build software, generate PDFs, etc.