protocol¶
Parse the @nix JSON protocol emitted by --log-format internal-json.
The protocol is a line-based JSON stream where each line starts with
@nix followed by a JSON object.
resFileLinked = 100
resBuildLogLine = 101
resUntrustedPath = 102
resCorruptedPath = 103
resSetPhase = 104
resProgress = 105
resSetExpected = 106
resPostBuildLogLine = 107
Built = 0
Substituted = 1
AlreadyValid = 2
PermanentFailure = 3
InputRejected = 4
OutputRejected = 5
TransientFailure = 6
CachedFailure = 7
TimedOut = 8
MiscFailure = 9
DependencyFailed = 10
LogLimitExceeded = 11
NotDeterministic = 12
ResolvesToAlreadyValid = 13
NoSubstituters = 14
BuildEvent
dataclass
¶
Collected information about one derivation build.
Source code in src/junix/protocol.py
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
cached = False
class-attribute
instance-attribute
¶
Whether the build was served from cache (substituted / already-valid).
True when Nix did not actually compile anything.
drv_path = ''
class-attribute
instance-attribute
¶
Store path of the derivation being built.
duration
property
¶
Wall-clock duration of the build in seconds, or None.
Computed as stopped_at - started_at when both are set.
The actBuild start event fires when Nix dispatches the
build, and the stop event fires when the build finishes
— the gap is the real build wall-clock as observed in the
event stream junix consumes. None for synthetic / cached
builds.
failed_dep_drv_path = None
class-attribute
instance-attribute
¶
When success is False because of a transitive dep failure
(not the build itself), the drv path of the failing dep that
caused the skip. None for direct build failures, cached
results, and successful builds. Populated by _ensure_builds
for synthetic entries created for dep-failed targets so the
JUnit <failure> can name the root cause and CI viewers
can group dep-failures separately from direct ones.
log_lines = field(default_factory=list)
class-attribute
instance-attribute
¶
Build log lines captured during the build.
name = ''
class-attribute
instance-attribute
¶
Human-readable name derived from drv_path.
phase = ''
class-attribute
instance-attribute
¶
Last reported build phase (e.g. unpackPhase, buildPhase).
result_code = None
class-attribute
instance-attribute
¶
Build result code (0=Built, 1=Substituted, 2=AlreadyValid, etc.).
None when not received (unknown / not reported by protocol).
started = False
class-attribute
instance-attribute
¶
Whether the activity start event was received.
started_at = None
class-attribute
instance-attribute
¶
time.monotonic() reading at the actBuild start event.
Set when NixEventHandler sees the start event for this
build's activity. None for synthetic / cached builds that
never produced a start event.
stopped = False
class-attribute
instance-attribute
¶
Whether the activity stop event was received.
stopped_at = None
class-attribute
instance-attribute
¶
time.monotonic() reading at the actBuild stop event.
Set when NixEventHandler sees the stop event for this
build's activity. None when the build never produced a stop
event (process crashed, etc.).
success = True
class-attribute
instance-attribute
¶
Whether the build completed successfully.
Defaults to True and set to False only if a failure is detected (e.g. process exits non-zero and build never stopped).
NixEventHandler
¶
Consume @nix protocol lines and produce a list of BuildEvent objects.
Typical usage:
handler = NixEventHandler()
for line in source:
handler.handle_line(line)
handler.finalize(process_successful=True)
for build in handler.builds:
...
Source code in src/junix/protocol.py
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 | |
builds
property
¶
Return the collected build events.
Call finalize() first to properly close any in-flight builds.
dep_failed_target_drv_paths
property
¶
Return drv paths of targets skipped due to a failed dep.
These are the targets whose drv path appeared in a
Cannot build '...'. Reason: 1 dependency failed msg.
They did not even start building, and the JUnit <failure>
for them should name the failing dep so users can click
through to the root cause.
downloaded_count
property
¶
Return the number of paths that were downloaded from cache.
Counts actCopyPath (type 100) start events, which Nix emits
for each individual path it downloads from a binary cache.
real_failure_drv_paths
property
¶
Return drv paths of builds that failed for their own reasons.
These are the drv paths that appeared in Cannot build '...'.
with a reason other than 1 dependency failed (e.g.
builder failed with exit code N, output rejected).
They are the candidates for the cause of the
dep_failed_target_drv_paths: when a run has exactly one
such drv, all dep-failed targets are attributed to it.
unbuildable_drv_paths
property
¶
Return the set of drv paths Nix said it could not build.
Populated from error: Cannot build '...'. msg events. A drv
path in this set was either reported as a real build failure
(builder failed) or a dep-failed target (1 dependency
failed). In both cases, every requested target that resolves
to one of these drv paths must NOT be reported as cached or
passed — they could not be built in this run.
_emit_stream_event(event, log_level, emit)
¶
Emit a single human-readable line for a parsed @nix event.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
dict[str, Any]
|
Parsed JSON object from an |
required |
log_level
|
int
|
Current verbosity threshold. |
required |
emit
|
Callable[[str], None]
|
Callback that receives lines to forward. |
required |
Source code in src/junix/protocol.py
_format_result_event(event)
¶
Format a result event as Nix-style human-readable output.
Returns None when the event should not be forwarded (unknown
activity, non-build activity, or unsupported result type).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event
|
dict[str, Any]
|
Parsed |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
Formatted line like |
Source code in src/junix/protocol.py
_mark_failed_from_error(error_msg)
¶
Parse a build error message and classify the failure.
Nix error messages for build failures contain the derivation path::
error: Cannot build '/nix/store/xxx-yyy.drv'.
Reason: <reason>.
We extract the derivation path and
- add it to
_unbuildable_drv_pathssorun_nix_buildcan detect dependent targets that never emitted anactBuildevent (Nix skipped them entirely because their dep failed); - append it to either
_dep_failed_target_drv_paths(Reason: 1 dependency failed— this drv is a target whose dep failed) or_real_failure_drv_paths(any other reason — this drv is a cause of the dep-failed targets); - mark the corresponding build activity as failed, since
the
stopevent for builds does not carry the result code in Nix 2.34.x.
Source code in src/junix/protocol.py
_strip_ansi(text)
staticmethod
¶
Remove ANSI escape sequences from text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
Text that may contain ANSI escape codes. |
required |
Returns:
| Type | Description |
|---|---|
str
|
Clean text with ANSI escapes removed. |
Source code in src/junix/protocol.py
finalize(process_successful=True)
¶
Mark any builds that never received a stop as failed.
Call this once the input stream is exhausted. If the overall Nix process failed (process_successful=False), any unstopped build is marked as a failure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
process_successful
|
bool
|
Whether the overall Nix process succeeded. |
True
|
Source code in src/junix/protocol.py
get_messages(threshold=0)
¶
Return log messages whose level is at or below the threshold.
Nix log levels (lower = more important):
0 = lvlError
1 = lvlWarn
2 = lvlNotice
3 = lvlInfo
4 = lvlTalkative
5 = lvlChatty
6 = lvlDebug
7 = lvlVomit
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
threshold
|
int
|
Maximum level to include (default 0 = errors only). |
0
|
Returns:
| Type | Description |
|---|---|
list[str]
|
List of message strings at or below the threshold. |
Source code in src/junix/protocol.py
handle_line(line)
¶
Process one line of text.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
line
|
str
|
A single line of text (without trailing newline). |
required |
Returns:
| Type | Description |
|---|---|
bool
|
True if the line was a recognised |
bool
|
(caller should forward it to stderr). |
Source code in src/junix/protocol.py
handle_stream(stream)
¶
Read an entire text stream (e.g. stdin) line by line.
Non-@nix lines are collected and returned so the caller can
forward them to stderr.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
IO[str]
|
A text-mode iterable (e.g. sys.stdin). |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
List of lines that did not start with |
Source code in src/junix/protocol.py
handle_streaming(stream, log_level=0, emit=None)
¶
Read a stream line by line and emit human-readable output immediately.
Non-@nix lines are emitted via emit as they are read. @nix
log messages and build log/phase events are emitted in human-readable
form when their level is at or below log_level.
The streaming behaviour is used by junix translate so that a long
build can be watched live. State is still updated so the JUnit report
can be produced after the stream ends.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
stream
|
IO[str]
|
Text stream to read (e.g. |
required |
log_level
|
int
|
Maximum Nix log level to forward to stderr. |
0
|
emit
|
Callable[[str], None] | None
|
Callable that receives each line to forward. Defaults to
printing to |
None
|
Source code in src/junix/protocol.py
_Activity
dataclass
¶
An in-flight protocol activity being tracked.
Source code in src/junix/protocol.py
result_code = None
class-attribute
instance-attribute
¶
Build result code captured from the stop event fields.
started_at = None
class-attribute
instance-attribute
¶
time.monotonic() reading at the start event (build activities only).
stopped_at = None
class-attribute
instance-attribute
¶
time.monotonic() reading at the stop event (build activities only).
store_path_to_name(store_path)
¶
Extract the human-readable derivation name from a Nix store path.
/nix/store/<hash>-<name>[-<version>] -> <name>[-<version>]
/nix/store/<hash>-<name>.drv -> <name>.drv
Uses the same logic as Nix's storePathToName().
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
store_path
|
str
|
A Nix store path (e.g. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The human-readable name portion of the path. |