Template
release/: JLCPCB order files when the repository makes them
A repository with .gitea/scripts/jlcpcb-files.py (harNES) gets <board>-<tag>-jlcpcb-bom.csv and -jlcpcb-cpl.csv (rotations corrected per LCSC part) in release/: the script runs with --force on a copy of hardware/ without the committed jlcpcb/ folders, so nothing stale is taken. When it refuses a board (DRC, missing correction), release/README.md and a workflow warning say why; the generic -cpl.csv then is the only placement file.
This commit is contained in:
1 parent
52a40c1c3e
commit
59f3df278b
2 files changed
+61
-5
No files matched your search
@@ -13,6 +13,8 @@ Writes into --out (default release/; existing content is replaced):
|
||||
<board>-<version>-pcb.pdf one page per copper, silkscreen and fab layer
|
||||
<board>-<version>-top.png 3D render, top
|
||||
<board>-<version>-bottom.png 3D render, bottom
|
||||
<board>-<version>-jlcpcb-bom.csv, -jlcpcb-cpl.csv JLCPCB order files with rotations
|
||||
corrected, when the repository has .gitea/scripts/jlcpcb-files.py (harNES)
|
||||
docs/<name>-<version>.pdf docs/*.md (pandoc, pdflatex) and docs/tex/*.tex (pdflatex)
|
||||
|
||||
Boards are the [[boards]] of proplan.toml, or hardware/*/*.kicad_pro when it names none. DRC and ERC
|
||||
@@ -190,7 +192,42 @@ def cpl(pcb, dest):
|
||||
return f"pick and place: {max(len(rows) - 1, 0)} parts, both sides, mm, DNP left out"
|
||||
|
||||
|
||||
def board(board_dir, pro, version, out, tmp):
|
||||
def jlcpcb_files(tmp):
|
||||
"""The repository's own JLCPCB order files, if it has .gitea/scripts/jlcpcb-files.py (harNES's pattern:
|
||||
JLCPCB BOM, CPL with rotations corrected per LCSC part, refused unless DRC is clean). It runs with
|
||||
--force on a copy of hardware/ without the committed jlcpcb/ folders, so nothing stale is taken.
|
||||
Returns {board folder relative to the root: (jlcpcb folder or None, why not)}."""
|
||||
script = ROOT / ".gitea" / "scripts" / "jlcpcb-files.py"
|
||||
if not script.exists():
|
||||
return {}
|
||||
work = tmp / "jlcpcb-work"
|
||||
shutil.copytree(ROOT / "hardware", work / "hardware", ignore=shutil.ignore_patterns("jlcpcb", "render", "*-backups"))
|
||||
(work / ".gitea" / "scripts").mkdir(parents=True)
|
||||
shutil.copyfile(script, work / ".gitea" / "scripts" / script.name)
|
||||
r = run([sys.executable, work / ".gitea" / "scripts" / script.name, "--force"], cwd=work, check=False)
|
||||
print(" " + "\n ".join((r.stdout + r.stderr).strip().splitlines()))
|
||||
blocks = {b.split("\n", 1)[0].split(":")[0].strip(): b for b in re.split(r"^== ", r.stdout, flags=re.M) if b.strip()}
|
||||
result = {}
|
||||
for pro in sorted(work.glob("hardware/*/*.kicad_pro")):
|
||||
folder = pro.parent / "jlcpcb"
|
||||
rel = pro.parent.relative_to(work).as_posix()
|
||||
if (folder / f"{pro.stem}-bom.csv").exists() and (folder / f"{pro.stem}-cpl.csv").exists():
|
||||
result[rel] = (folder, "")
|
||||
else:
|
||||
lines = [l.strip() for l in blocks.get(pro.stem, "").splitlines()[1:] if l.strip()]
|
||||
i = next((k for k, l in enumerate(lines) if l.startswith("NOT written")), None)
|
||||
if i is None: # no verdict: the script crashed (its last line says why)
|
||||
why = (r.stderr.strip().splitlines() or ["jlcpcb-files.py failed"])[-1]
|
||||
else: # "NOT written: DRC is not clean" or "NOT written:", then one line per problem
|
||||
head, rest = lines[i].partition(":")[2].strip(), lines[i + 1:]
|
||||
why = "; ".join(x for x in (head, rest[0] if rest else "") if x) or "NOT written"
|
||||
if len(rest) > 1:
|
||||
why += f" (+{len(rest) - 1} more)"
|
||||
result[rel] = (None, why[:300])
|
||||
return result
|
||||
|
||||
|
||||
def board(board_dir, pro, version, out, tmp, jlc):
|
||||
"""Write one board's files; returns (name, [(file, what)], checks)."""
|
||||
name = pro.stem
|
||||
sch, pcb = board_dir / f"{name}.kicad_sch", board_dir / f"{name}.kicad_pcb"
|
||||
@@ -210,6 +247,17 @@ def board(board_dir, pro, version, out, tmp):
|
||||
f"{d['schematic_parity']} schematic parity issues")
|
||||
files.insert(0, (f"{base}-gerbers.zip", gerbers_zip(pcb, dest / f"{base}-gerbers.zip", tmp)))
|
||||
files.append((f"{base}-cpl.csv", cpl(pcb, dest / f"{base}-cpl.csv")))
|
||||
rel = board_dir.relative_to(ROOT).as_posix()
|
||||
if rel in jlc:
|
||||
folder, why = jlc[rel]
|
||||
if folder:
|
||||
for kind, what in (("bom", "JLCPCB BOM: one line per LCSC part number"),
|
||||
("cpl", "JLCPCB CPL: rotations and centres corrected for JLCPCB's footprints")):
|
||||
shutil.copyfile(folder / f"{name}-{kind}.csv", dest / f"{base}-jlcpcb-{kind}.csv")
|
||||
files.append((f"{base}-jlcpcb-{kind}.csv", what))
|
||||
files[-3] = (files[-3][0], files[-3][1] + "; KiCad rotations, for JLCPCB use the -jlcpcb-cpl.csv")
|
||||
else:
|
||||
checks.append(f"JLCPCB files not made ({why})")
|
||||
copper = ",".join(copper_layers(pcb))
|
||||
cli("pcb", "export", "pdf", "--mode-multipage", "--include-border-title",
|
||||
"--layers", f"{copper},F.Silkscreen,B.Silkscreen,F.Fab,B.Fab", "--common-layers", "Edge.Cuts",
|
||||
@@ -358,6 +406,11 @@ def main():
|
||||
kicad = cli("version").strip()
|
||||
clear(out)
|
||||
boards, skipped, ok = [], [], True
|
||||
jlc_tmp = tempfile.TemporaryDirectory()
|
||||
jlc = {}
|
||||
if (ROOT / ".gitea" / "scripts" / "jlcpcb-files.py").exists():
|
||||
print("== JLCPCB order files (.gitea/scripts/jlcpcb-files.py)")
|
||||
jlc = jlcpcb_files(Path(jlc_tmp.name))
|
||||
for board_dir, pro in projects(m):
|
||||
rel = board_dir.relative_to(ROOT).as_posix()
|
||||
if pro is None:
|
||||
@@ -367,7 +420,7 @@ def main():
|
||||
print(f"== {pro.stem}")
|
||||
with tempfile.TemporaryDirectory() as t:
|
||||
try:
|
||||
name, files, checks = board(board_dir, pro, a.version, out, Path(t))
|
||||
name, files, checks = board(board_dir, pro, a.version, out, Path(t), jlc)
|
||||
except Failed as e:
|
||||
print(f"::error::{pro.stem}: {e}")
|
||||
ok = False
|
||||
@@ -375,10 +428,11 @@ def main():
|
||||
boards.append((name, files, checks))
|
||||
for c in checks:
|
||||
print(f" {c}")
|
||||
if re.search(r"[1-9]\d* (errors|unconnected|schematic)", c):
|
||||
if re.search(r"[1-9]\d* (errors|unconnected|schematic)", c) or c.startswith("JLCPCB"):
|
||||
print(f"::warning::{name} {c}")
|
||||
for f, _ in files:
|
||||
print(f" hardware/{name}/{f}")
|
||||
jlc_tmp.cleanup()
|
||||
print("== documentation")
|
||||
foot = tex_escape(f"{product} {a.version}, {commit}, {when[:10]}")
|
||||
docs_made, docs_failed = documents(a.version, foot, out)
|
||||
|
||||
Reference in new issue
Block a user