mirror of
https://github.com/diodeinc/pcb.git
synced 2026-09-14 20:17:19 +08:00
v0.4.51
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ee4e7e2b90 |
Feat: Add local routing using FreeRouting (#936)
* Add local FreeRouting auto-routing engine (pcb route --engine freerouting)
Routes boards locally via a FreeRouting Java JAR, as an alternative to
DeepPCB cloud routing. Squashed from the branch's original 29-commit
history (which included an earlier --remote-flag design, a REST-API
rewrite, and a revert back to a refined REST-API design) into one
commit for a clean rebase onto upstream/main.
- pcb route --engine freerouting|deeppcb (deeppcb remains default)
- Auto-downloads and SHA-256-verifies a pinned FreeRouting JAR
(also verifies a $PATH-discovered JAR; --fr-jar/FREEROUTING_JAR are
explicit overrides and skip verification)
- Drives FreeRouting's local REST API (not one-shot CLI mode) so
Ctrl+C and timeouts can recover partial routing progress, working
around a confirmed upstream bug where CLI mode's TIMED_OUT state
never reaches COMPLETED
- All work happens on a temp copy of the board + project file; the
original is only overwritten once a validated result is ready
- Requires Java 21+ on $PATH; does not auto-manage a JRE/JDK
- Test sandbox now exempts loopback from its dead-proxy egress block,
needed for tests that spawn a local FreeRouting API server
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Keep pcb route hidden from top-level command list
Re-adds #[command(hide = true)] on Commands::Route, which was dropped
at some point. Avoids needing to regenerate the pcbc help snapshot,
and keeps route --engine freerouting unlisted while local FreeRouting
routing is still new.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Preserve cached partial output when losing contact with FreeRouting
poll_job hard-bailed after 20 consecutive get_job failures, discarding
last_known_output even though it held real progress cached from an
earlier RUNNING poll. A crashed or unreachable server lost that
progress entirely, unlike the cancel/timeout paths which already
return it. Return it here too (still treated as a Cancelled outcome
for execute() to publish) instead of only when we still have a live
connection to ask for it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Warn (non-fatally) when --fr-jar/FREEROUTING_JAR don't match the pinned hash
Both are explicit user overrides, so a mismatch isn't rejected outright
(may be an intentional dev/patched build), but running an unverified
JAR with zero visibility wasn't right either. Surface a warning instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Validate --fr-timeout range before dispatching to FreeRouting
fr_timeout had no bounds checking, unlike DeepPCB's timeout (rejected
above 60 min). Out-of-range values could panic (Instant + Duration
overflow for huge values) or produce a malformed job_timeout
(format_hms hour field >= 24). Reject anything outside 1..=3540
seconds (the documented 59-minute cap) up front with a clear error.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Align --fr-timeout cap with DeepPCB's 60-minute cap (3600s)
Was 3540s (59 min); use 3600s (60 min) to match DeepPCB's timeout cap.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Honor Ctrl+C outside the FreeRouting poll loop
CANCEL was only ever read inside poll_job, so pressing Ctrl+C during
export_dsn or import_ses (zone fill) printed "Stopping FreeRouting and
fetching the best result so far..." but the step ran to completion
anyway with no way to abort short of SIGKILL.
Check CANCEL after export_dsn (bail before starting FreeRouting at
all) and after import_ses (publish whatever was imported and stop
before opening KiCad), so the flag is honored at both phase
boundaries outside the poll loop too.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Shorten freerouting CHANGELOG entry to match other one-liners
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Trim unnecessary comments
Removed step-numbering and code-restating comments in
freerouting/mod.rs and tests/route.rs; shortened the FREEROUTING_VERSION
doc comment to its essential rationale. Kept comments explaining
non-obvious invariants, races, and upstream quirks.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Use reqwest instead of shelling out to curl in freerouting tests
resolve_freerouting_jar's download loop and wait_for_http_ok both
shelled out to curl, reinventing HTTP calls already made correctly
elsewhere in this same PR (download_to_file in freerouting/mod.rs,
FreeroutingApiClient::wait_ready in freerouting/api.rs) using reqwest,
already a dependency of this crate. The curl version was also
strictly worse for the JAR download: no retry/backoff and no hash
verification.
Left the curl_json helper in test_freerouting_cancel_returns_output
as-is — it deliberately drives the raw API via curl to test the wire
protocol independent of our own reqwest-based client, not an
oversight.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Replace raw \r pass-counter with an animated Spinner
The poll loop printed " pass N\r" via a bare print!/flush with no
line-clearing, which could leave stray/garbled characters behind
depending on terminal state (e.g. overlapping with a previous longer
line) and gave no visible feedback between pass updates otherwise.
Reuse the Spinner component already used elsewhere in this file
(Exporting DSN..., Starting FreeRouting...) instead of hand-rolling
terminal control: it animates continuously via indicatif, updates its
message with the current pass number, and clears/finishes cleanly
with a success or warning icon depending on whether the run completed
or was cancelled/timed out.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Remove redundant doc comment on JobOutput
"Result of fetching a job's output" just restates the enum's name;
its variants already carry their own explanatory docs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Scope the sandbox's loopback proxy bypass to the client that needs it
Replaces the blanket NO_PROXY=127.0.0.1,localhost exemption (which
widened every sandboxed command's "no accidental egress" guarantee to
"any loopback service is reachable") with the client-side .no_proxy()
opt-out FreeroutingApiClient already uses in production.
wait_for_http_ok now builds its own no_proxy() client instead of
relying on the ambient NO_PROXY var, matching that pattern, so the
sandbox's NO_PROXY can go back to "" (nothing depended on the
exemption: the sandbox env only applies to `sandbox.run("pcbc", ...)`
calls, and the only such call that reaches loopback already opts out
explicitly).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Simplify FreeRouting spinner message; hide pass count behind --debug
"Running FreeRouting (timeout: 300s)... pass 0" surfaces two pieces
of internal detail with no clear meaning to a user watching the CLI:
the configured timeout doesn't change moment-to-moment, and "pass N"
is FreeRouting's internal routing-iteration count, not something most
users can act on or interpret.
Drop the timeout from the spinner message entirely, and only append
the pass count when --debug is set (checked via the log crate's
active level, which env_logger already raises to debug when --debug
is passed) rather than showing it unconditionally.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Fix misleading "FreeRouting finished in Xs" message on timeout/cancel
Reported: running against a real board, the CLI printed "! FreeRouting
finished in 300.1s" (with a warning icon) immediately followed by "No
routing progress to save. Board left untouched." — "finished" reads as
success, contradicting the very next line saying nothing was produced.
The message didn't distinguish a genuine completion from a timeout/
cancel with zero output.
Split the message by outcome and whether any output was captured:
"finished in Xs" only for a real RunOutcome::Completed, "stopped after
Xs (partial result)" when cancelled/timed out but something was
salvaged, and "stopped after Xs with no routing progress" when
nothing was ever produced — so the message matches what actually
happened instead of always sounding like success.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Show elapsed time in the FreeRouting spinner while running
The spinner sat on a static "Running FreeRouting..." for the whole
run with no visible feedback beyond the animation itself. Update the
message every ~1s poll tick with elapsed seconds ("Running
FreeRouting... 45s elapsed"), regardless of whether the pass count
changed, so long stretches on one pass still show visible progress.
Under --debug, also appends the current pass number.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Show pass N/max as a rough progress signal in the spinner
FreeRouting's job settings already fix max_passes at 200 via
update_settings; extracted to a shared FREEROUTING_MAX_PASSES constant
so poll_job can show "pass N/200" alongside elapsed time instead of
just the bare pass number, giving a rough (FreeRouting can finish
earlier via its own improvement_threshold, so not exact) sense of how
close a run is to done. Shown by default now rather than gated behind
--debug, since it's cheap and more informative than a bare elapsed
timer.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Only show pass N/max once past 0; drop "Local" from the routing message
FreeRouting's documented API (docs/API/API_v1.md, confirmed against
context7's indexed copy) has no current_pass field at all in the
GET /v1/jobs/{jobId} schema — only max_passes as a config input under
router_settings, not a live counter. In practice a static "pass
0/200" was observed sitting unchanged for the whole run on some
boards (likely the undifferentiated initial routing stage, which
runs before any numbered optimization pass starts), which reads as
stuck/broken rather than as progress. Only display the pass fraction
once it's actually > 0 — elapsed time alone still updates every tick
either way, so the spinner never goes fully static.
Also drop "Local" from "Local routing X via FreeRouting": redundant
now that --engine freerouting is the only local engine and DeepPCB
already reads as the cloud option by contrast.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Bump Java version gate to 25 to match FreeRouting v2.2.4's requirement
FreeRouting v2.2.4's build.gradle targets JavaVersion.VERSION_25, so
the pinned jar fails to load (UnsupportedClassVersionError) on
anything older. resolve_java/is_java_21_plus only gated on Java 21+
and the error message told users to install OpenJDK 21, so a user
with Java 21-24 passed our prerequisite check and then hit a confusing
crash when FreeRouting's API server died at launch instead of a clear
"install Java 25+" error upfront.
Extracted REQUIRED_JAVA_VERSION as a named constant (kept in sync with
FREEROUTING_VERSION) instead of hardcoding 21 in three places, and
renamed is_java_21_plus to is_java_version_sufficient since it no
longer names a fixed version. Updated the duplicated check/messages in
tests/route.rs to match.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Show download percentage progress for the FreeRouting JAR
try_download_to_file previously used a single io::copy with no
visibility into a ~50-70MB download's progress beyond a static
"Downloading..." line. Stream the response in chunks against
Content-Length using the existing pcb_ui::ProgressBar component
(already used elsewhere in this file via Spinner) instead, falling
back to the old plain io::copy when the server doesn't report a
Content-Length (e.g. chunked encoding).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Human-readable byte sizes and a versioned message on the download bar
Use indicatif's {bytes}/{total_bytes} template placeholders (auto-
formats sizes like "2.3 MiB") instead of the default template's raw
{pos}/{len} byte counts. Also include the pinned version in the
message ("Downloading FreeRouting (2.2.4)"), matching how Homebrew
Cask labels its downloads.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Fix rustfmt violations and convert cancel-test from curl to reqwest
cargo fmt was failing CI on freerouting/api.rs, freerouting/mod.rs, and
route.rs. Also switches the cancel-job integration test off shelling out
to curl, matching the reqwest::blocking already used elsewhere in the
test file.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Trim explanatory comments in sandbox, route, and route tests
Cuts down verbose justification comments to the essentials, keeping
only the ones that carry genuinely non-obvious information (e.g. the
FreeRouting UUID header requirement, java -version writing to stderr).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Check Java before auto-downloading the FreeRouting JAR; make --fr-timeout minutes
Splits JAR resolution so the explicit --fr-jar/FREEROUTING_JAR path is
still validated immediately, but the PATH search and network
auto-download fall back only after Java is confirmed installed —
previously a user without Java would pay for the full JAR download
before being told the run can't proceed anyway, and it made
test_freerouting_java_not_found vacuous.
Also switches --fr-timeout to minutes (default 5, max 60), matching
--timeout's format and validation style instead of a seconds range.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Don't mislabel a completed route as partial on late Ctrl+C
The post-import CANCEL check tested the sticky global Ctrl+C flag
instead of the authoritative outcome from run_freerouting. Pressing
Ctrl+C during the "Importing SES..." step after FreeRouting had
already fully completed would report "Partial result saved," skip the
timing summary, and skip opening KiCad even though the board was fully
routed. Removing the early return lets the common path below use
outcome to report correctly in all cases.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Avoid concurrent JAR download races; clarify --fr-timeout comments
Suffix the FreeRouting JAR temp download path with the process ID so
two concurrent `pcb route` runs don't interleave writes into the same
.tmp file and spuriously fail the SHA-256 check. Also reword comments
that described format_hms's sub-minute precision as reachable via
--fr-timeout, which is always minute-aligned in practice.
* Stage sibling .kicad_dru when routing so zone fill honors custom rules
Custom design rules live in <project>.kicad_dru, not the .kicad_pro
file. The local FreeRouting path staged only the board and project
copies, so the DRC engine used by import_ses's zone fill fell back to
default clearances and published fills that ignored the board's
custom rules.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Don't report a completed route as success when final output fetch fails
poll_job's Completed branch silently fell back to last_known_output
(a stale snapshot from an earlier RUNNING poll) when the terminal
get_output call failed, while still reporting RunOutcome::Completed.
That published a partially-routed board over the user's original
file while printing "Result saved" instead of "Partial result
saved". Now a failed final fetch downgrades the outcome to
Cancelled so the messaging matches what's actually written to disk.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Harden FreeRouting cache dir against local-user tampering on /tmp fallback
When dirs::cache_dir() is unavailable, the JAR cache fell back to a
fixed, predictable /tmp/pcb/freerouting path that another local user
could pre-create with hostile permissions/ownership. Restrict newly
created cache dirs to owner-only (0700) and refuse to trust one owned
by a different uid.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Throttle poll-loop output refresh to reduce overhead on large boards
get_output makes the FreeRouting server serialize the whole
in-progress board to SES and base64-encode it. poll_job was calling
it on every ~1s poll tick while the job was in progress, forcing a
full serialize/encode/decode cycle every second for the entire
(up to 60 minute) run. Throttle refreshes to once every 5s instead;
the cache only needs to be recent enough to survive a cancel/output
race, not perfectly up to date.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Don't report a fully-routed board's clean completion as a fetch failure
poll_job's JobState::Completed branch used best_effort_output(), which
collapsed the API's JobOutput::NothingToRoute (a legitimate "nothing
left to route" result) into the same None as a genuine output-fetch
error. A board where every net was already connected therefore saw
"finished but its final output could not be fetched" and had its
outcome downgraded from Completed to Cancelled, reporting "stopped ...
with no routing progress" for a run that actually succeeded.
Match on JobOutput directly so NothingToRoute stays a clean Completed
result with no SES to write, and reserve the warning + downgrade for
real fetch failures.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Actually refuse the FreeRouting cache dir when owned by another user
restrict_dir_to_owner only warned on an ownership mismatch and let
find_or_download_freerouting_jar proceed anyway, so the /tmp fallback
path's doc comment ("refuse to reuse it if it's owned by someone
else") didn't match behavior — an attacker-owned cache dir was still
downloaded/cached into. The dir's contents were still hash-verified,
but the directory itself stayed attacker-controlled (pre-placed
files, .tmp tampering, rename races).
restrict_dir_to_owner now returns whether the dir is safely owned by
us; a mismatch bails out with a clear error instead of silently
downloading into it. The ownership check also now runs before the
cached-JAR reuse path, not just before a fresh download, since reuse
had skipped the check entirely.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Scope the FreeRouting temp-dir cache fallback by uid
The dirs::cache_dir() fallback used a fixed pcb/freerouting path
under the shared system temp dir, so any two local users landed on
the same cache location and had to fight over ownership (handled by
the ownership-mismatch bailout added previously). Scope the fallback
by effective uid (pcb-<uid>/freerouting) so different users never
share the path in the first place; restrict_dir_to_owner remains as
defense in depth rather than the primary mechanism.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Give get_output a longer timeout than status/control endpoints
The API client used one flat 20s timeout for every request, but
get_output's cost is proportional to board size (the server
serializes and base64-encodes the whole in-progress or final board),
unlike the cheap status/control calls. On a large board, a genuinely
completed job's final output fetch could exceed 20s, which poll_job's
Completed branch treated the same as "nothing to save" and fell back
to a possibly-empty last_known_output — silently discarding a
successful route. get_output now overrides the client default with a
120s per-request timeout.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Temporarily pin FreeRouting JAR to AdamMomen/freerouting v2.2.5
Upstream freerouting/freerouting has been slow to cut releases off
main, so point the auto-download/PATH-search integrity check at a
fork's v2.2.5 release (built off upstream's release head) as a
stopgap. FREEROUTING_REPO is a single constant, so reverting to
freerouting/freerouting (with a re-pinned SHA-256 for the matching
upstream release) once upstream catches up is a one-line change.
See PR discussion for the full rationale.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Extract execute_freerouting to match execute_deeppcb's pattern
route::execute inlined the freerouting engine's timeout validation
and board resolution directly in the match arm, unlike the deeppcb
arm which delegates to its own execute_deeppcb function. Extract the
same shape for consistency.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Restore explanatory comment on sandbox network-blocking env vars
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Drop DeepPCB comparison from FreeRouting changelog entry
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Trim comments in freerouting/api.rs
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Don't let get_output's 120s timeout block Ctrl+C responsiveness
get_output's timeout was raised to 120s for the terminal
post-completion fetch, but that same long budget also applied to the
opportunistic in-progress refresh and cancel-path fetches inside
poll_job's loop, so a slow response there could delay noticing
CANCEL for up to 120s. get_output now takes an explicit per-call
timeout: GET_OUTPUT_TIMEOUT (120s) only for the one-shot completed
fetch, DEFAULT_TIMEOUT (20s) for the in-loop refresh and
best_effort_output.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Update integration tests to use the pinned FreeRouting fork/version
Tests were downloading freerouting-2.2.4.jar from upstream
freerouting/freerouting, while production now pins AdamMomen/freerouting
v2.2.5 with a different hash/URL — so boot, cancel, and end-to-end
coverage exercised a different artifact than what users actually get.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Trim comments in freerouting/mod.rs
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Pin FreeRouting to official upstream v2.3.0 release
Upstream now ships the stack overflow fixes previously only available
on a temporary fork, so switch back to freerouting/freerouting.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Simplify FreeRouting output polling per review feedback
Fetch /output once at our own deadline (or Ctrl+C), then cancel,
instead of maintaining a 5s-refreshed output cache throughout the
run. FreeRouting's own job_timeout now trails our deadline by a
safety margin instead of matching it exactly, since it's just a
backstop for when our own cancel request fails to land.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Run FreeRouting in its own process group so Ctrl+C doesn't kill it early
Java previously inherited pcb's foreground process group, so the terminal's
SIGINT went straight to it and it could exit before the poll loop fetched
partial output. A second Ctrl+C now force-kills it immediately.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Add unit tests for FreeRouting process-group detachment
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Handle FreeRouting's TERMINATED job state
Add the missing TERMINATED variant and treat it like CANCELLED/TIMED_OUT:
fetch whatever partial route exists instead of hard-failing the run.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Fix incorrect FreeRouting v2.3.0 jar SHA-256 pin
The pinned checksum didn't match the actual published release
artifact, so integrity verification would fail on every download.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
* Revert "Fix incorrect FreeRouting v2.3.0 jar SHA-256 pin"
This reverts commit
|
||
|
|
ca29a43006 |
Update dependencies (#931)
Co-authored-by: Amp <amp@ampcode.com> |
||
|
|
11d664c6ec |
[SIM] more powerful tools (#588)
* add inline setup * output netlist to stdout * use ngspice to run sim directly * add lsp support for sim * fix span detection * sim file relative * fix clippy * improved backward compatibility * fix snapsho * add changelog * fail on missing models * address comments * add timeout * condense timeout * add DNP support to sim * address dual eval concerns * better help text --------- Co-authored-by: d-asnaghi <30296575+hexdae@users.noreply.github.com> |
||
|
|
021390901e | Bump workspace to Rust 2024 edition (#563) | ||
|
|
f1d40b0bb3 |
[FEATURE] sync track and vias (#110)
* sync vias and track * cleanup tests * add test * simplify virtual classes * sync vias and track * cleanup tests * add test * simplify virtual classes * sync vias and track * cleanup tests * add test * simplify virtual classes * cleaner stdio handling * use list for kicad iters to avoid windows locks * explicitly delete board * cleanup resources * update snap * Add Windows warning and disable Windows layout tests * update formatting * update formatting --------- Co-authored-by: AnandKapadia <anand.kapadia1@gmail.com> Co-authored-by: Lenny Khazan <lenny.khazan@gmail.com> |
||
|
|
f75787dbf3 | Standardize crate metadata (#10) | ||
|
|
e852477546 |
Initial release (#5)
* Initial release * cargo-dist * build and test CI * update ubuntu workflow * fmt * ubuntu: install deps * clippy: 0.1.88 fixes * format * install clippy * Update README.md * Don't run cargo-dist on PRs |