Files
grabowski 59f3df278b 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.
2026-10-16 19:01:20 +07:00

271 lines
12 KiB
Markdown

# B4L product repository template
Every B4L product repository has the same layout, so people and proplan find
the product image, the boards, the firmware and the libraries in the same
place. This file explains the layout, the rules and `proplan.toml`. It is
copied into every repository created from the template; keep it as the
reference.
## Create a product repository
In Gitea: **New Repository**, choose the template **B4L/product-template**,
tick **Git Content**, give the repository the product's name and a
description. Gitea puts the name into `README.md`, `CHANGELOG.md`,
`proplan.toml` and the board folder under `hardware/` (the files listed in
`.gitea/template`). Then:
1. Replace `assets/product.png` with a photo or render of the product (PNG,
JPEG or WebP, at most 5 MB).
2. Create the KiCad project in the board folder (KiCad: File, New Project; the
project file is `hardware/<name>/<name>.kicad_pro`).
3. Commit and push. The KiBot workflow checks the board on every push.
4. Tag the first prototype `v0.1` when its boards are ordered, and link the
tag to the stage on the project's Prototypes tab in proplan. CI then puts
the stage's manufacturing files and documentation into `release/`.
## Layout
| Path | What goes there |
|---|---|
| `proplan.toml` | the manifest proplan reads (below) |
| `README.md`, `CHANGELOG.md` | what the product is; one changelog section per stage (`## v0.2`) |
| `release/` | the newest stage's manufacturing files and documentation, written by CI (below) |
| `assets/product.png` | the product image |
| `hardware/<board>/` | one KiCad project per board |
| `hardware/lib/` | symbols, footprints and 3D models shared by the boards |
| `firmware/<target>/` | MCU code (STM32CubeIDE or CMake), `firmware/lib/` for shared drivers |
| `external/` | third-party code as git submodules (e.g. ST's USB-PD middleware) |
| `software/<tool>/` | host tools and apps |
| `mech/` | FreeCAD sources and STEP exports |
| `sim/` | LTspice/ngspice, calculation scripts and notebooks |
| `test/<stage>/` | bench results per stage (`test/v0.1/`) |
| `docs/`, `docs/datasheets/`, `docs/tex/` | documentation, vendor PDFs, LaTeX sources |
| `.gitea/workflows/` | KiBot, release outputs, fabrication document |
## Rules
- A board revision is a tag (`v0.1`, `v0.2`), never a copied folder
(`kicad_v1_5/` next to `kicad_v1_6/`). A real variant built side by side
(48 V and 24 V, 2-board and 3-board) is its own folder under `hardware/` and
its own `[[boards]]` entry.
- Generated output (gerbers, BOMs, production zips, PDFs, plots) is built by CI
and attached to the release; it is not committed. The one exception is
`release/`, which CI fills at a stage tag: nothing goes there by hand, and
generated files go nowhere else.
- Vendor datasheets go to `docs/datasheets/`, not the repository root.
- Third-party code is a git submodule in `external/` and is not edited here.
- IDE build output and tool caches (`Debug/`, `__pycache__/`, `.claude/`,
KiCad backups) stay out; `.gitignore` covers them.
## Versioning
One repository, three kinds of tags. The prefix of a firmware or software
tag is the folder it versions, so every target and every tool has its own
releases, and the board release workflow (`v*`) never reacts to them.
| What | Tag | Example | Scheme |
|---|---|---|---|
| Prototype stage (the whole product) | `vMAJOR.MINOR` | `v0.1`, `v0.2` | the next stage takes the next number; the tag proplan links to a stage and CI attaches the board outputs to |
| Firmware target `firmware/<target>/` | `firmware/<target>/vMAJOR.MINOR.PATCH` | `firmware/main-mcu/v1.2.0`, `firmware/main-mcu/v1.3.0-rc.1` | SemVer 2.0 (semver.org) |
| Software tool `software/<tool>/` | `software/<tool>/YYYY.MM.MICRO` | `software/diag/2026.10.0`, `software/diag/2026.10.1` | CalVer (calver.org) |
Firmware (SemVer):
- MAJOR when something outside the firmware has to change with it: CAN IDs
or message layout, the protocol the host tools speak, the format of
settings stored in flash, the bootloader interface. MINOR for new features
that keep all of that, PATCH for fixes.
- `0.y.z` while prototyping (anything may change), `1.0.0` with the first
production firmware. Bench builds before a release are pre-releases
(`v1.3.0-rc.1`, `-rc.2`).
- The version is also where the build reads it (CMake
`project(<name> VERSION 1.2.0)` or a `version.h`), and the firmware reports
it at run time (CAN, USB or UART), so a board on the bench says what it runs.
Software (CalVer `YYYY.MM.MICRO`):
- Year, month without a leading zero (calver.org's `MM`), and MICRO counting
the releases of that month from 0: `2026.10.0`, `2026.10.1`, `2026.11.0`.
Without the zero the number is also a valid version for pip
(`pyproject.toml`) and Cargo.
- A date, not a compatibility promise: a tool's README says which firmware
MAJOR versions it talks to.
For all three:
- The tag is the source of truth. A version written in a file
(`pyproject.toml`, `Cargo.toml`, CMake, `version.h`) matches the tag and
changes in the commit that gets tagged.
- A tag is never moved or reused; a bad release gets the next version.
proplan flags a moved tag on its stage card.
- Each firmware target and each tool keeps its own `CHANGELOG.md` in its
folder. The root `CHANGELOG.md` has one section per prototype stage and
lists the firmware and software versions that stage was built with.
- Firmware and software releases are made by hand in Gitea for now (push the
tag, then New Release); `release.yml` builds the release files for `v*` tags
only.
## Release files (`release/`)
`release/` always holds the newest prototype stage, ready to send to a
manufacturer or a customer: whoever needs the current boards or documents
takes them from there, without KiCad and without knowing the tags. CI writes
it (`.gitea/workflows/release.yml`, `.gitea/scripts/release-files.py`) when a
stage tag is pushed; nobody edits it.
| Path | What |
|---|---|
| `release/README.md` | tag, commit, date, KiCad version, ERC and DRC result per board, the file list with sizes and SHA-256, the stage's CHANGELOG section |
| `release/hardware/<board>/<board>-<tag>-gerbers.zip` | Gerbers (every copper layer, paste, silkscreen, mask, outline; Protel names, no X2) and Excellon drill files (mm, PTH and NPTH apart): what a PCB fab such as JLCPCB takes |
| `release/hardware/<board>/<board>-<tag>-bom.csv` | bill of materials grouped by value, footprint, manufacturer, MPN and LCSC; DNP parts left out |
| `release/hardware/<board>/<board>-<tag>-cpl.csv` | pick and place (KiCad position file, mm, both sides, DNP left out) |
| `release/hardware/<board>/<board>-<tag>-schematic.pdf`, `-pcb.pdf` | the schematic; the PCB with one page per copper, silkscreen and fab layer |
| `release/hardware/<board>/<board>-<tag>-top.png`, `-bottom.png` | 3D renders |
| `release/hardware/<board>/<board>-<tag>-jlcpcb-bom.csv`, `-jlcpcb-cpl.csv` | only in a repository with `.gitea/scripts/jlcpcb-files.py` (harNES): JLCPCB's BOM and a CPL with rotations corrected per LCSC part, made only from a clean DRC; use these, not `-cpl.csv`, for JLCPCB assembly |
| `release/docs/<name>-<tag>.pdf` | every `docs/*.md` except `README.md` and `input.md` (pandoc), and every `docs/tex/*.tex` with a `\documentclass` (pdflatex); the footer carries product, tag, commit and date |
- Boards are the `[[boards]]` of `proplan.toml` (or `hardware/*/*.kicad_pro`
when it names none), so a repository that adopted the layout without moving
folders gets its release files too.
- The tag is in every file name: a file sent to a fab or forwarded by mail
still says which stage it is.
- The same files, plus one zip of the whole folder, are attached to the tag's
release, so older stages stay one click away. `release/` follows the newest
stage tag only: a tag of an older stage or a pre-release (`v0.3-rc.1`) gets
its release attachments but leaves `release/` as it is.
- ERC and DRC errors do not stop the release (the tag records what was
ordered), but they are printed in `release/README.md` and as warnings on the
workflow run. Check the KiBot run before tagging.
- A document that does not build is listed in `release/README.md` as not
built; characters pdflatex cannot print (emoji) come out as `?` and are
named there.
- The commit says `[skip ci]` and `release/` is outside every workflow's
paths, so it starts no other workflow. `.gitattributes` keeps the files
byte for byte (`release/** -text`), so the SHA-256 match on Windows too.
- Locally, from the repository root, in the CI image (Docker):
`docker run --rm -v "$PWD:/w" -w /w git.b4l.co.th/b4l/kicad10_auto_node:latest python3 .gitea/scripts/release-files.py v0.1 --out build/release`
writes the same files to `build/` (ignored by git).
Not in it (yet): firmware binaries (firmware has its own tags), STEP models
(`mech/`). JLCPCB order files need `jlcpcb-files.py` and a
`jlcpcb-corrections.csv` per board (harNES has both); without them the generic
`-cpl.csv` carries KiCad's rotations, which JLCPCB's footprints may not share.
## proplan.toml
proplan reads this file and the image it names, nothing else of the
repository's content: at the default branch for the Code tab, and at a stage's
tagged commit for the stage's card (so the card shows the product as it was at
that tag). Every key is optional.
```toml
schema = 1 # 1; a newer schema is refused
[product]
name = "papir" # default: the repository's name
image = "assets/product.png" # PNG, JPEG or WebP, at most 5 MB; none when left out
[[boards]] # one per KiCad project, as many as needed
name = "papir" # default: the folder's last part
path = "hardware/papir" # required
[paths] # each default: the folder of the same name
firmware = "firmware"
software = "software"
mech = "mech"
sim = "sim"
test = "test"
docs = "docs"
external = "external"
```
Paths are relative to the repository root (no leading `/`, no `..`). A board,
a path written under `[paths]` or the image that does not exist is reported on
the Code tab ("proplan.toml: missing ..."); a path
left out that does not exist is simply not shown. Unknown keys are ignored.
## Adopting the layout in an existing repository
An existing repository does not have to move anything: a `proplan.toml` that
names the current folders is enough, and history and open work stay as they
are. Moving folders to the template's names can follow later.
papir (KiCad project in `papir/`, firmware in `code/`):
```toml
[[boards]]
name = "papir"
path = "papir"
[paths]
firmware = "code"
software = "pirnet"
mech = "mech"
docs = "docs"
```
LVSolarBuck (the current board in `kicad48V-R/LVSolarBuck`, firmware in `code64/`):
```toml
[product]
image = "housing.png"
[[boards]]
name = "LVSolarBuck"
path = "kicad48V-R/LVSolarBuck"
[paths]
firmware = "code64"
sim = "ltspice"
test = "testing"
docs = "docs"
```
PowerModule (three board variants):
```toml
[product]
image = "images/render-display.png"
[[boards]]
name = "PowerModule"
path = "kicad/PowerModule"
[[boards]]
name = "PowerModule48"
path = "kicad/PowerModule48"
[[boards]]
name = "PowerModule48PD"
path = "kicad/PowerModule48PD"
[paths]
firmware = "code"
software = "tools"
sim = "ltspice"
test = "testing"
docs = "tex"
external = "external"
```
## CI
- `.gitea/workflows/kibot.yml`: on a push that touches `hardware/`, KiBot runs
ERC and DRC and builds schematic PDF, gerbers, drill files, pick and place
and BOM for every board (`hardware/*/*.kicad_pro`); the outputs are kept as a
workflow artifact.
- `.gitea/workflows/release.yml`: on a tag `v*`, builds the release files
(above) with kicad-cli, pandoc and pdflatex, attaches them to the tag's
release (created when missing, with the stage's CHANGELOG section) and, for
the newest stage, commits them to `release/` on main. The Actions token must
be allowed to write releases and to push to main (if main is protected, its
push rules must let the Actions bot through). Firmware and software tags
(`firmware/...`, `software/...`) do not start it.
- `.gitea/workflows/docgen.yml`: with a `docs/input.md` (typecad-docgen front
matter, as in PowerModule), builds the fabrication document of the first
board and pushes it to the `docu` branch.
All three use the runner label `kicad10_auto` (as LVSolarBuck). They were
adapted from LVSolarBuck and PowerModule and run for the first time when the
first board is committed and tagged; check that first run.