rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 heartwood18342392a0b51acb997b2769db6fbcf27471f229
{
"request": "trigger",
"version": 1,
"event_type": "patch",
"repository": {
"id": "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
"name": "heartwood",
"description": "Radicle Heartwood Protocol & Stack",
"private": false,
"default_branch": "master",
"delegates": [
"did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT",
"did:key:z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW",
"did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"did:key:z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz",
"did:key:z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz"
]
},
"action": "Updated",
"patch": {
"id": "63b4e1d9046a2af75401f74234d7fc18b447109d",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"title": "Canonical Symbolic References",
"state": {
"status": "open",
"conflicts": []
},
"before": "547a753768b73f0a375f9648ba3e057a4f650e3a",
"after": "18342392a0b51acb997b2769db6fbcf27471f229",
"commits": [
"18342392a0b51acb997b2769db6fbcf27471f229",
"ff5c8a167c11c79f37feeb6000b7e6592bc4fa76",
"7f1f2767f54807c497febf9dbaad1cc00c6f974e",
"2d626cecfbc8b300b5960e6ddb9b5f39f8ca2bb6",
"1da2ae460cc030db07c1256903593ede75f519a2",
"e035902a6db8936e21bb1d62af3657df519b3602",
"a8cf4080ce7f4442fa4ad3adc2b907a2fe04d55b",
"c9c75621fad41dc4e1339012f758b270f94bb23b",
"78922b3d075bee6c572869c546c5533a14b36c61",
"efb78efd1658f34d9d9e639db38044c34dde434b",
"bb55b79bb986e6f0df0d83efd85260b78614a912",
"540827acec99775e951b01e36290639f6463d460",
"259b4d37257e5b886696b3b976544529cfceccf6",
"1bf20f8b05bfdef317b3ae2187812e1feff59d2c"
],
"target": "ac3eba09a111c09c3a6e4ce106520a3aaa1808b2",
"labels": [],
"assignees": [],
"revisions": [
{
"id": "63b4e1d9046a2af75401f74234d7fc18b447109d",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Extend the payload `xyz.radicle.crefs` to additionally support creating symbolic references via the member \"symbolic\".",
"base": "86472fdccbf95d08d0184776ee1ca75d01caf2c8",
"oid": "0d8ccb51101df44aa643c14e421f0a8f5a1aca53",
"timestamp": 1758036727
},
{
"id": "a8f4547661b3e0c0ae87a7b2bf10bac784529d42",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "First working revision.",
"base": "86472fdccbf95d08d0184776ee1ca75d01caf2c8",
"oid": "4910c5f03beefe20d7a1dea9e0c986de867987b5",
"timestamp": 1758063904
},
{
"id": "4728a5335d735afa5c6a0427bd88a35f93c7b8fa",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "REVIEW\n\nSuper nice set of changes, and seems to fit so nicely into the canonical\nreference system!\n\nAppreciate the type safety too :)\n\nLeft some bits of feedback, but all-in-all I'm very happy with these changes.",
"base": "86472fdccbf95d08d0184776ee1ca75d01caf2c8",
"oid": "cc99c8cc509107757916307fdc5f29210ef70114",
"timestamp": 1758111930
},
{
"id": "61f41e3e3bf3469acd72b9b3f237bb29356f328f",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Work in Fintan's review.",
"base": "86472fdccbf95d08d0184776ee1ca75d01caf2c8",
"oid": "72584651f1818dfc2e9759d4f4afa60ad0ada357",
"timestamp": 1758117424
},
{
"id": "4f8b72024cc6e070f6e8324292b8205536f6f924",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Rebase",
"base": "7b00bf2e3ac5e83eab262182bf51a5ede656145c",
"oid": "3324ebbb01151e6f0db951155d342a1782c27f95",
"timestamp": 1758120326
},
{
"id": "e4e142e1b405905ca7219e1d2534619408104095",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Handle interactions between rules and symrefs, and cyclic symrefs.",
"base": "e70850cb36c5f08d2d8714017d5404dde34deefa",
"oid": "902d99f2131f8420f2a8a9c70f645f69e13cfbf1",
"timestamp": 1758459092
},
{
"id": "6a1155bceb7d3ca55b39311848111f8fdc1f1552",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "REVIEW\n\nSee commits for review suggestions.\n\nLooking really good and clean still!",
"base": "e70850cb36c5f08d2d8714017d5404dde34deefa",
"oid": "ce4864fc46c4463b6aeabb79b0712bcaf9987bdf",
"timestamp": 1758559598
},
{
"id": "52de3142d005a0d934cf6d84f3dab33f2b94dcc0",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Squash in Fintan's review, rename tests as mentioned on Zulip, improve docs on `Doc::canonical_refs`.",
"base": "e70850cb36c5f08d2d8714017d5404dde34deefa",
"oid": "0891ad955e8a68b689b56a1b5e1a9e1077147586",
"timestamp": 1758614851
},
{
"id": "d76a072ff1259a4a1e584e93d5e4eb47a1ff3d27",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Rebase",
"base": "ed8b086045ee5d7bd1327f579de7861a1cf49e3b",
"oid": "0b0f73d2b39088bbfeb30c496e870b27c915a81b",
"timestamp": 1758725112
},
{
"id": "17797c5586de8002dbd28d8c647ebc9268430ee0",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Rebase",
"base": "4787b53b1e85d8052744fc77e4160e4d90e46d0f",
"oid": "d02c4c127a1ff0a8a2d56c8dcad3d815b6cd9427",
"timestamp": 1759255430
},
{
"id": "9956ae65550991f8b8bed09349588e828e8099c1",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Rebase",
"base": "ee49e28766ce7c703b95e22d177cce046072f03d",
"oid": "79f3230dd0d29fbf79d931a9adea19855d5f7448",
"timestamp": 1759333073
},
{
"id": "785d6bf241783c872f468b25c030686ecc9f812b",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Push broken rebase. Will fix in future revision.",
"base": "c06b00e330d82c8b8221cc8f8776c883208d159f",
"oid": "b438d5e5cb8e13817aa6db3b6e2e1b2915f71c43",
"timestamp": 1771121329
},
{
"id": "80f4aaecdea67b0d1588f7ad6de8b4c6e62d375e",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Rebase",
"base": "d9915d275fd07d20db08cf7d3488f8650e66b88a",
"oid": "50d1ef9c3e793ad084b63719869e915d7bdfa539",
"timestamp": 1773957804
},
{
"id": "9336db89237741cf36ac82b899c3daabe35d0fde",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Fixes, stricter validation.",
"base": "80c1bba76f432e70d6e766fe750bea70554cfe86",
"oid": "3dc532fd4038ec5179e642dd6f132cce5667626d",
"timestamp": 1775764455
},
{
"id": "84b9f1f4eb20bb10d78e70c5a108a52b185533e0",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "Rebase and add a CLI test.",
"base": "547a753768b73f0a375f9648ba3e057a4f650e3a",
"oid": "259b4d37257e5b886696b3b976544529cfceccf6",
"timestamp": 1776275398
},
{
"id": "e7649fcac7abefbe21efb751812f83e72b53f89b",
"author": {
"id": "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
"alias": "fintohaps"
},
"description": "REVIEW",
"base": "547a753768b73f0a375f9648ba3e057a4f650e3a",
"oid": "18342392a0b51acb997b2769db6fbcf27471f229",
"timestamp": 1776331835
}
]
}
}
{
"response": "triggered",
"run_id": {
"id": "7cd05d0d-27df-4be6-a215-8460a6240a7c"
},
"info_url": "https://cci.rad.levitte.org//7cd05d0d-27df-4be6-a215-8460a6240a7c.html"
}
Started at: 2026-04-16 11:30:40.689321+02:00
Commands:
$ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5 .
✓ Creating checkout in ./...
✓ Remote cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT added
✓ Remote-tracking branch cloudhead@z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/master created for z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT
✓ Remote cloudhead@z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW added
✓ Remote-tracking branch cloudhead@z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW/master created for z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW
✓ Remote fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM added
✓ Remote-tracking branch fintohaps@z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM/master created for z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM
✓ Remote erikli@z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz added
✓ Remote-tracking branch erikli@z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz/master created for z6MkgFq6z5fkF2hioLLSNu1zP2qEL1aHXHZzGH1FLFGAnBGz
✓ Remote lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz added
✓ Remote-tracking branch lorenz@z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz/master created for z6MkkPvBfjP4bQmco5Dm7UGsX2ruDBieEHi8n9DVJWX5sTEz
✓ Repository successfully cloned under /opt/radcis/ci.rad.levitte.org/cci/state/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/
╭────────────────────────────────────╮
│ heartwood │
│ Radicle Heartwood Protocol & Stack │
│ 154 issues · 41 patches │
╰────────────────────────────────────╯
Run `cd ./.` to go to the repository directory.
Exit code: 0
$ rad patch checkout 63b4e1d9046a2af75401f74234d7fc18b447109d
✓ Switched to branch patch/63b4e1d at revision 84b9f1f
✓ Branch patch/63b4e1d setup to track rad/patches/63b4e1d9046a2af75401f74234d7fc18b447109d
Exit code: 0
$ git config advice.detachedHead false
Exit code: 0
$ git checkout 18342392a0b51acb997b2769db6fbcf27471f229
HEAD is now at 18342392 REVIEW: Restructure canonical_refs into explicit state machine
Exit code: 0
$ rad patch show 63b4e1d9046a2af75401f74234d7fc18b447109d -p
╭──────────────────────────────────────────────────────────────────────────────────╮
│ Title Canonical Symbolic References │
│ Patch 63b4e1d9046a2af75401f74234d7fc18b447109d │
│ Author lorenz z6MkkPv…WX5sTEz │
│ Head 259b4d37257e5b886696b3b976544529cfceccf6 │
│ Base 547a753768b73f0a375f9648ba3e057a4f650e3a │
│ Branches patch/63b4e1d │
│ Commits ahead 2, behind 13 │
│ Status open │
│ │
│ Extend the payload `xyz.radicle.crefs` to additionally support creating symbolic │
│ references via the member "symbolic". │
├──────────────────────────────────────────────────────────────────────────────────┤
│ 259b4d3 cli/test: Canonical References │
│ 1bf20f8 radicle/crefs: Support Symbolic References │
├──────────────────────────────────────────────────────────────────────────────────┤
│ ● Revision 63b4e1d @ 0d8ccb5 by lorenz z6MkkPv…WX5sTEz 6 months ago │
│ ↑ Revision a8f4547 @ 4910c5f by lorenz z6MkkPv…WX5sTEz 6 months ago │
│ ↑ Revision 4728a53 @ cc99c8c by fintohaps z6Mkire…SQZ3voM 6 months ago │
│ ↑ Revision 61f41e3 @ 7258465 by lorenz z6MkkPv…WX5sTEz 6 months ago │
│ ↑ Revision 4f8b720 @ 3324ebb by fintohaps z6Mkire…SQZ3voM 6 months ago │
│ ↑ Revision e4e142e @ 902d99f by lorenz z6MkkPv…WX5sTEz 6 months ago │
│ ↑ Revision 6a1155b @ ce4864f by fintohaps z6Mkire…SQZ3voM 6 months ago │
│ ↑ Revision 52de314 @ 0891ad9 by lorenz z6MkkPv…WX5sTEz 6 months ago │
│ ↑ Revision d76a072 @ 0b0f73d by lorenz z6MkkPv…WX5sTEz 6 months ago │
│ ↑ Revision 17797c5 @ d02c4c1 by lorenz z6MkkPv…WX5sTEz 6 months ago │
│ ↑ Revision 9956ae6 @ 79f3230 by lorenz z6MkkPv…WX5sTEz 6 months ago │
│ ↑ Revision 785d6bf @ b438d5e by lorenz z6MkkPv…WX5sTEz 1 month ago │
│ ↑ Revision 80f4aae @ 50d1ef9 by lorenz z6MkkPv…WX5sTEz 3 weeks ago │
│ ↑ Revision 9336db8 @ 3dc532f by lorenz z6MkkPv…WX5sTEz 6 days ago │
│ ↑ Revision 84b9f1f @ 259b4d3 by lorenz z6MkkPv…WX5sTEz 15 hours ago │
│ ↑ Revision e7649fc @ 1834239 by fintohaps z6Mkire…SQZ3voM 7 seconds ago │
╰──────────────────────────────────────────────────────────────────────────────────╯
commit 259b4d37257e5b886696b3b976544529cfceccf6
Author: Lorenz Leutgeb <lorenz.leutgeb@radicle.xyz>
Date: Wed Apr 15 19:02:23 2026 +0200
cli/test: Canonical References
Extend the test for canonical tags to also cover canonical branches and
canonical symbolic references.
diff --git a/crates/radicle-cli/examples/git/git-push-canonical-branch.md b/crates/radicle-cli/examples/git/git-push-canonical-branch.md
new file mode 100644
index 000000000..c45a10dc8
--- /dev/null
+++ b/crates/radicle-cli/examples/git/git-push-canonical-branch.md
@@ -0,0 +1,90 @@
+``` ~alice
+$ rad id update --title "Add canonical branch for releases" --payload xyz.radicle.crefs rules '{ "refs/heads/releases/*": { "threshold": 1, "allow": [ "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk" ] }, "refs/tags/*": { "threshold": 1, "allow": "delegates" }, "refs/tags/qa/*": { "threshold": 1, "allow": "delegates" }}'
+✓ Identity revision [..] created
+╭────────────────────────────────────────────────────────────────────────╮
+│ Title Add canonical branch for releases │
+│ Revision 37a1aad231100cd206c49aed79e405ea2da9204b │
+│ Blob bbefd77cfeb456e500ad868c3b4effe1f7f818e2 │
+│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
+│ State active │
+│ Quorum no │
+├────────────────────────────────────────────────────────────────────────┤
+│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
+│ ? did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk bob │
+╰────────────────────────────────────────────────────────────────────────╯
+
+@@ -1,26 +1,32 @@
+ {
+ "payload": {
+ "xyz.radicle.crefs": {
+ "rules": {
++ "refs/heads/releases/*": {
++ "allow": [
++ "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
++ ],
++ "threshold": 1
++ },
+ "refs/tags/*": {
+ "allow": "delegates",
+- "threshold": 2
++ "threshold": 1
+ },
+ "refs/tags/qa/*": {
+ "allow": "delegates",
+ "threshold": 1
+ }
+ }
+ },
+ "xyz.radicle.project": {
+ "defaultBranch": "master",
+ "description": "Radicle Heartwood Protocol & Stack",
+ "name": "heartwood"
+ }
+ },
+ "delegates": [
+ "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
+ "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
+ ],
+ "threshold": 1
+ }
+```
+
+``` ~bob
+$ cd heartwood
+$ rad sync -f
+Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from the network, found 1 potential seed(s).
+✓ Target met: 1 seed(s)
+🌱 Fetched from z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+$ rad id accept 37a1aad231100cd206c49aed79e405ea2da9204b -q
+```
+
+Bob immediately pushes a branch that matches the rule, thus populating
+modifying the canonical namespace.
+
+``` ~bob
+$ git checkout -b releases/2
+$ git commit --allow-empty --message "Release notes for version 2"
+[releases/2 afec366] Release notes for version 2
+```
+
+``` ~bob (stderr)
+$ git push -u rad releases/2
+✓ Canonical reference refs/heads/releases/2 updated to target commit afec366785ed3651cdc66975c0fec41866c9ce62
+✓ Synced with 1 seed(s)
+To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+ * [new branch] releases/2 -> releases/2
+```
+
+``` ~alice
+$ rad sync -f
+Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from the network, found 1 potential seed(s).
+✓ Target met: 1 seed(s)
+🌱 Fetched from z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+$ git ls-remote rad
+f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 HEAD
+f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
+afec366785ed3651cdc66975c0fec41866c9ce62 refs/heads/releases/2
+f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/tags/qa/v2.1
+ac51a0746a5e8311829bc481202909a1e3acc0c2 refs/tags/v1.0-hotfix
+89f935f27a16f8ed97915ade4accab8fe48057aa refs/tags/v2.0
+```
\ No newline at end of file
diff --git a/crates/radicle-cli/examples/git/git-push-canonical-symbolic-ref.md b/crates/radicle-cli/examples/git/git-push-canonical-symbolic-ref.md
new file mode 100644
index 000000000..55a85e8e8
--- /dev/null
+++ b/crates/radicle-cli/examples/git/git-push-canonical-symbolic-ref.md
@@ -0,0 +1,130 @@
+Bob overhears that the new default name for the default branch is "main",
+not "master" as it used to be.
+To make tooling that expect "main" work, without the hassle of having to push
+to such branch manually, he opts to create a symbolic reference.
+
+``` ~bob
+$ cd heartwood
+$ rad id update --title "Add canonical symbolic ref" --payload xyz.radicle.crefs symbolic '{ "refs/heads/main": "refs/heads/master" }'
+✓ Identity revision [..] created
+╭────────────────────────────────────────────────────────────────────────╮
+│ Title Add canonical symbolic ref │
+│ Revision 62e2cb60c6df9ad9908b6697b5d126760a855484 │
+│ Blob b20de7b184673eb0d9227be17640c923d8ef3f3e │
+│ Author did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk │
+│ State active │
+│ Quorum no │
+├────────────────────────────────────────────────────────────────────────┤
+│ ✓ did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk bob (you) │
+│ ? did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice │
+╰────────────────────────────────────────────────────────────────────────╯
+
+@@ -1,32 +1,35 @@
+ {
+ "payload": {
+ "xyz.radicle.crefs": {
+ "rules": {
+ "refs/heads/releases/*": {
+ "allow": [
+ "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
+ ],
+ "threshold": 1
+ },
+ "refs/tags/*": {
+ "allow": "delegates",
+ "threshold": 1
+ },
+ "refs/tags/qa/*": {
+ "allow": "delegates",
+ "threshold": 1
+ }
++ },
++ "symbolic": {
++ "refs/heads/main": "refs/heads/master"
+ }
+ },
+ "xyz.radicle.project": {
+ "defaultBranch": "master",
+ "description": "Radicle Heartwood Protocol & Stack",
+ "name": "heartwood"
+ }
+ },
+ "delegates": [
+ "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
+ "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
+ ],
+ "threshold": 1
+ }
+```
+
+Alice is happy with the new revision and accepts it.
+
+``` ~alice
+$ rad sync -f
+Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from the network, found 1 potential seed(s).
+✓ Target met: 1 seed(s)
+🌱 Fetched from z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
+$ rad id accept 62e2cb60c6df9ad9908b6697b5d126760a855484 -q
+```
+
+As usual, alice works on "master".
+
+``` ~alice
+$ git commit --allow-empty --message "Whew, new feature!"
+[master 4dc510d] Whew, new feature!
+```
+
+And updating the canonical reference for "master" also works as usual.
+
+``` ~alice (stderr)
+$ git push rad
+✓ Canonical reference refs/heads/master updated to target commit 4dc510ddea5fd66499d1d2e996b8a97c8d57be54
+✓ Synced with 1 seed(s)
+To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+ f2de534..4dc510d master -> master
+```
+
+Then, Alice is curious about the new symbolic reference.
+She inspects the remote and sees that indeed a new branch named "main" now exists.
+
+``` ~alice
+$ git ls-remote rad
+4dc510ddea5fd66499d1d2e996b8a97c8d57be54 HEAD
+4dc510ddea5fd66499d1d2e996b8a97c8d57be54 refs/heads/main
+4dc510ddea5fd66499d1d2e996b8a97c8d57be54 refs/heads/master
+afec366785ed3651cdc66975c0fec41866c9ce62 refs/heads/releases/2
+f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/tags/qa/v2.1
+ac51a0746a5e8311829bc481202909a1e3acc0c2 refs/tags/v1.0-hotfix
+89f935f27a16f8ed97915ade4accab8fe48057aa refs/tags/v2.0
+```
+
+Of course, she can also fetch it to her working copy as usual.
+
+``` ~alice (stderr)
+$ git fetch rad
+From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji
+ * [new branch] main -> rad/main
+ * [new branch] releases/2 -> rad/releases/2
+ * [new tag] qa/v2.1 -> rad/tags/qa/v2.1
+ * [new tag] qa/v2.1 -> qa/v2.1
+```
+
+Bob fetches Alice's changes.
+
+``` ~bob
+$ rad sync -f
+Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from the network, found 1 potential seed(s).
+✓ Target met: 1 seed(s)
+🌱 Fetched from z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
+```
+
+And, sure enough, there is the new branch just as he wanted it.
+
+``` ~bob (stderr)
+$ git fetch rad
+From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji
+ * [new branch] main -> rad/main
+ f2de534..4dc510d master -> rad/master
+```
+
+Note that neither Alice nor Bob pushed directly to "main".
\ No newline at end of file
diff --git a/crates/radicle-cli/tests/commands/git.rs b/crates/radicle-cli/tests/commands/git.rs
index a8b3c58c8..9ab6eb1e5 100644
--- a/crates/radicle-cli/tests/commands/git.rs
+++ b/crates/radicle-cli/tests/commands/git.rs
@@ -301,8 +301,12 @@ fn git_push_canonical_lightweight_tags() {
.unwrap();
}
+/// This test exercises a large surface of the "Canonical References" feature:
+/// - Annotated Tags
+/// - Branches
+/// - Symbolic References
#[test]
-fn git_push_canonical_annotated_tags() {
+fn git_push_canonical() {
let mut environment = Environment::new();
let alice = environment.node("alice");
let bob = environment.node("bob");
@@ -341,4 +345,40 @@ fn git_push_canonical_annotated_tags() {
)
.run()
.unwrap();
+
+ formula(
+ &environment.tempdir(),
+ "examples/git/git-push-canonical-branch.md",
+ )
+ .unwrap()
+ .home(
+ "alice",
+ environment.work(&alice),
+ [("RAD_HOME", alice.home.path().display())],
+ )
+ .home(
+ "bob",
+ environment.work(&bob),
+ [("RAD_HOME", bob.home.path().display())],
+ )
+ .run()
+ .unwrap();
+
+ formula(
+ &environment.tempdir(),
+ "examples/git/git-push-canonical-symbolic-ref.md",
+ )
+ .unwrap()
+ .home(
+ "alice",
+ environment.work(&alice),
+ [("RAD_HOME", alice.home.path().display())],
+ )
+ .home(
+ "bob",
+ environment.work(&bob),
+ [("RAD_HOME", bob.home.path().display())],
+ )
+ .run()
+ .unwrap();
}
commit 1bf20f8b05bfdef317b3ae2187812e1feff59d2c
Author: Lorenz Leutgeb <lorenz.leutgeb@radicle.xyz>
Date: Tue Sep 16 22:02:09 2025 +0200
radicle/crefs: Support Symbolic References
Canonical references can not be used to model symbolic references.
Relax this restriction by adding another member to the payload, named
"symbolic". Its key-value/name-target pairs then translate directly to
canonical symbolic references.
Re-use `Unprotected` just like rules do.
Care is taken to not allow circular references, and that there always
is a rule that could generate the target of a symbolic reference
(or a chain of symbolic references). Still, a symbolic reference may
dangle (for example when its target reference cannot be computed
because of divergence), but at least it can be prevented that a
symref *always* dangles, because there is no rule that would produce
its target.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 010e7fff4..568fc761a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -283,6 +283,8 @@ With the introduction of `clap`, this helped with the introduction of a command
environment variable `RAD_PASSPHRASE` (lower priority than the
credential). The identifier of the credential is
"xyz.radicle.node.passphrase".
+- Symbolic references can now be handled by canonical references by coding them
+ in the payload `xyz.radicle.crefs` under the key `symbolic`.
## Fixed Bugs
diff --git a/crates/radicle-node/src/worker/fetch.rs b/crates/radicle-node/src/worker/fetch.rs
index f143d98d7..c8420a175 100644
--- a/crates/radicle-node/src/worker/fetch.rs
+++ b/crates/radicle-node/src/worker/fetch.rs
@@ -14,8 +14,7 @@ use radicle::prelude::RepoId;
use radicle::storage::git::Repository;
use radicle::storage::refs::RefsAt;
use radicle::storage::{
- ReadRepository, ReadStorage as _, RefUpdate, RemoteRepository, RepositoryError,
- WriteRepository as _,
+ ReadRepository, ReadStorage as _, RefUpdate, RemoteRepository, WriteRepository as _,
};
use radicle::{Storage, cob, git, node};
use radicle_fetch::git::refs::Applied;
@@ -128,15 +127,6 @@ impl Handle {
// points to a repository that is temporary and gets moved by [`mv`].
let repo = storage.repository(rid)?;
repo.set_identity_head()?;
- match repo.set_head_to_default_branch() {
- Ok(()) => {
- log::trace!(target: "worker", "Set HEAD successfully");
- }
- Err(RepositoryError::Quorum(e)) => {
- log::warn!(target: "worker", "Fetch could not set HEAD for {rid}: {e}")
- }
- Err(e) => return Err(e.into()),
- }
let canonical = match set_canonical_refs(&repo, &applied) {
Ok(updates) => updates.unwrap_or_default(),
@@ -346,8 +336,21 @@ fn set_canonical_refs(
repo: &Repository,
applied: &Applied,
) -> Result<Option<UpdatedCanonicalRefs>, error::Canonical> {
+ const LOG_MESSAGE: &str = "set-canonical-reference from fetch (radicle)";
+
let identity = repo.identity()?;
- let rules = identity.doc().canonical_refs()?.rules().clone();
+ let crefs = identity.doc().canonical_refs()?;
+
+ for (name, target) in crefs.symbolic().iter() {
+ if let Err(e) = repo.set_symbolic_ref(name, target, LOG_MESSAGE) {
+ log::warn!(
+ target: "worker",
+ "Failed to set canonical symbolic reference '{name}' → '{target}': {e}"
+ );
+ }
+ }
+
+ let rules = crefs.rules().clone();
let mut updated_refs = UpdatedCanonicalRefs::default();
let refnames = applied
@@ -389,12 +392,10 @@ fn set_canonical_refs(
refname, object, ..
}) => {
let oid = object.id();
- if let Err(e) = repo.backend.reference(
- refname.clone().as_str(),
- oid.into(),
- true,
- "set-canonical-reference from fetch (radicle)",
- ) {
+ if let Err(e) =
+ repo.backend
+ .reference(refname.clone().as_str(), oid.into(), true, LOG_MESSAGE)
+ {
log::warn!(
target: "worker",
"Failed to set canonical reference {refname}->{oid}: {e}"
diff --git a/crates/radicle-remote-helper/src/push.rs b/crates/radicle-remote-helper/src/push.rs
index 0ec93a226..180ff565e 100644
--- a/crates/radicle-remote-helper/src/push.rs
+++ b/crates/radicle-remote-helper/src/push.rs
@@ -258,6 +258,8 @@ pub(super) fn run(
git: &impl GitService,
node: &mut impl NodeSession,
) -> Result<Vec<String>, Error> {
+ const LOG_MESSAGE: &str = "set-canonical-reference from git-push (radicle)";
+
// Don't allow push if either of these conditions is true:
//
// 1. Our key is not in ssh-agent, which means we won't be able to sign the refs.
@@ -290,9 +292,6 @@ pub(super) fn run(
}
}
let delegates = stored.delegates()?;
- let identity = stored.identity()?;
- let project = identity.project()?;
- let canonical_ref = git::refs::branch(project.default_branch());
let mut set_canonical_refs: Vec<(git::fmt::Qualified, git::canonical::Object)> =
Vec::with_capacity(specs.len());
@@ -407,6 +406,8 @@ pub(super) fn run(
if !ok.is_empty() {
let _ = stored.sign_refs(&signer)?;
+ stored.set_canonical_symbolic_refs(LOG_MESSAGE)?;
+
for (refname, object) in &set_canonical_refs {
let oid = object.id();
let kind = object.object_type();
@@ -419,20 +420,11 @@ pub(super) fn run(
)
};
- // N.b. special case for handling the canonical ref, since it
- // creates a symlink to HEAD
- if *refname == canonical_ref {
- stored.set_head_to_default_branch()?;
- }
-
match stored.backend.refname_to_id(refname.as_str()) {
Ok(new) if oid != new => {
- stored.backend.reference(
- refname.as_str(),
- oid.into(),
- true,
- "set-canonical-reference from git-push (radicle)",
- )?;
+ stored
+ .backend
+ .reference(refname.as_str(), oid.into(), true, LOG_MESSAGE)?;
print_update();
}
Err(e) if e.code() == git::raw::ErrorCode::NotFound => {
diff --git a/crates/radicle/src/git/canonical.rs b/crates/radicle/src/git/canonical.rs
index 13af6b494..3143b8b6e 100644
--- a/crates/radicle/src/git/canonical.rs
+++ b/crates/radicle/src/git/canonical.rs
@@ -12,6 +12,7 @@ mod voting;
pub mod effects;
pub mod protect;
pub mod rules;
+pub mod symbolic;
pub use rules::{MatchedRule, RawRule, Rules, ValidRule};
diff --git a/crates/radicle/src/git/canonical/protect.rs b/crates/radicle/src/git/canonical/protect.rs
index 4326da5e4..581c4cd74 100644
--- a/crates/radicle/src/git/canonical/protect.rs
+++ b/crates/radicle/src/git/canonical/protect.rs
@@ -19,7 +19,7 @@ pub enum Error {
}
/// A witnesses that the inner reference-like value is not protected.
-#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize)]
+#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, Hash)]
#[repr(transparent)]
#[serde(transparent)]
pub(super) struct Unprotected<T: RefLike>(T);
@@ -42,6 +42,13 @@ impl<T: RefLike> Unprotected<T> {
pub fn into_inner(self) -> T {
self.0
}
+
+ /// Allows creation without any checking. Callers must ensure that
+ /// `reflike` is indeed unprotected!
+ #[inline]
+ const fn new_unchecked(reflike: T) -> Self {
+ Self(reflike)
+ }
}
impl<T: RefLike> AsRef<T> for Unprotected<T> {
@@ -68,7 +75,10 @@ impl<T: RefLike + std::fmt::Display> std::fmt::Display for Unprotected<T> {
/// For types that are commonly used in conjunction with [`Unprotected`]
/// have some `impl`s and companion infallible injections.
mod impls {
- use crate::git::fmt::{RefString, refspec::QualifiedPattern};
+ use crate::git::{
+ fmt::{Qualified, RefStr, RefString, refname, refspec::QualifiedPattern},
+ refs::branch,
+ };
use super::*;
@@ -76,6 +86,33 @@ mod impls {
/// means to be [`RefLike`].
impl RefLike for RefString {}
+ impl Unprotected<RefString> {
+ /// The reference name `HEAD`.
+ // We would like to have a `pub const HEAD`, but
+ // [`crate::git::RefStr::from_str`] is private.
+ #[inline]
+ pub fn head() -> Self {
+ // Calling [`Unprotected::new_unchecked`] here is legal,
+ // because we know statically that `HEAD` is not protected.
+ Unprotected::new_unchecked(refname!("HEAD"))
+ }
+ }
+
+ /// [`Qualified`] is a restriction on [`RefString`].
+ impl RefLike for Qualified<'_> {}
+
+ impl Unprotected<Qualified<'_>> {
+ /// Construct a qualified reference name for given branch, i.e.,
+ /// return `/refs/heads/<name>`
+ pub fn branch(name: &RefStr) -> Self {
+ Self::new(branch(name)).expect("branches are never protected")
+ }
+
+ pub fn to_ref_string(&self) -> Unprotected<RefString> {
+ Unprotected::new_unchecked(self.0.to_ref_string())
+ }
+ }
+
/// A [`QualifiedPattern`] is [`RefLike`] in the sense that it matches a
/// (possibly infinite) set of [`crate::git::Qualified`].
impl RefLike for QualifiedPattern<'_> {}
diff --git a/crates/radicle/src/git/canonical/rules.rs b/crates/radicle/src/git/canonical/rules.rs
index 50fac2d46..debe87005 100644
--- a/crates/radicle/src/git/canonical/rules.rs
+++ b/crates/radicle/src/git/canonical/rules.rs
@@ -390,6 +390,22 @@ impl From<Did> for Allowed {
}
}
+impl std::fmt::Display for Allowed {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ match self {
+ Allowed::Delegates => f.write_str("\"allowed\""),
+ Allowed::Set(dids) => {
+ let dids = dids
+ .iter()
+ .map(|did| did.to_string())
+ .collect::<Vec<_>>()
+ .join("\", \"");
+ f.write_fmt(format_args!("[\"{dids}\"]"))
+ }
+ }
+ }
+}
+
/// A marker `enum` that is used in a [`ValidRule`].
///
/// It ensures that a rule that has been deserialized, resolving the `delegates`
diff --git a/crates/radicle/src/git/canonical/symbolic.rs b/crates/radicle/src/git/canonical/symbolic.rs
new file mode 100644
index 000000000..83d676afa
--- /dev/null
+++ b/crates/radicle/src/git/canonical/symbolic.rs
@@ -0,0 +1,389 @@
+//! Symbolic references, which link neither to nor from protected references.
+//! The prototypical example is `HEAD → refs/heads/main`.
+
+use indexmap::IndexMap;
+use serde::{Deserialize, Serialize};
+use thiserror::Error;
+
+use crate::git::fmt::Qualified;
+use crate::git::fmt::RefString;
+
+use super::protect::Unprotected;
+
+use reachability::reachable;
+
+pub type RawName = RefString;
+
+/// Names of symbolic references are unprotected references.
+/// Requiring [`Unprotected`] makes symbolic references that link *from*
+/// protected references unrepresentable.
+pub(super) type Name = Unprotected<RefString>;
+
+impl std::cmp::Ord for Name {
+ fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+ self.as_ref().cmp(other.as_ref())
+ }
+}
+
+impl std::cmp::PartialOrd for Name {
+ fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+pub type RawTarget = RefString;
+
+/// Targets for symbolic references are unprotected references.
+/// Requiring [`Unprotected`] makes symbolic references that link *to*
+/// protected references unrepresentable.
+pub(super) type Target = Unprotected<RefString>;
+
+/// Maintains a cycle-free set of symbolic references.
+/// Note that dangling references are not detected.
+#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(try_from = "IndexMap<Name, Target>")]
+pub struct SymbolicRefs(IndexMap<Name, Target>);
+
+/// Read-only access.
+impl SymbolicRefs {
+ /// Returns an iterator over all contained symbolic references, as pairs of
+ /// their name [`RawName`] and [`RawTarget`].
+ pub fn iter(&self) -> impl Iterator<Item = (&RawName, &RawTarget)> {
+ self.0
+ .iter()
+ .map(|(name, target)| (name.as_ref(), target.as_ref()))
+ }
+
+ /// Returns an iterator over all contained symbolic references, as pairs of
+ /// their name [`RawName`] and resolved [`RawTarget`].
+ pub fn iter_resolved(&self) -> impl Iterator<Item = (&RawName, &RawTarget)> {
+ self.iter_resolved_unprotected()
+ .map(|(name, target)| (name.as_ref(), target.as_ref()))
+ }
+
+ pub(super) fn iter_resolved_unprotected(&self) -> impl Iterator<Item = (&Name, &Target)> {
+ self.0
+ .keys()
+ .filter_map(|name| self.resolve_unprotected(name).map(|target| (name, target)))
+ }
+
+ fn resolve_unprotected<'a, 'b>(&'a self, mut name: &'b Name) -> Option<&'a Target>
+ where
+ 'a: 'b,
+ {
+ while let Some(target) = self.0.get(name) {
+ match self.0.get(target) {
+ Some(next) => {
+ name = next;
+ }
+ None => return Some(target),
+ }
+ }
+
+ None
+ }
+
+ /// Returns `true` if the set of symbolic references is empty.
+ pub fn is_empty(&self) -> bool {
+ self.0.is_empty()
+ }
+}
+
+/// Utilities for handling of `HEAD`.
+impl SymbolicRefs {
+ /// Construct [`SymbolicRefs`] for the single symbolic reference `HEAD`
+ /// targeting `/refs/heads/<branch_name>`.
+ // This exists to encapsulate [`Unprotected`].
+ pub fn head(branch_name: &RefString) -> Self {
+ let mut result = Self::default();
+ result
+ .try_insert_unprotected(
+ Unprotected::head().to_owned(),
+ Unprotected::branch(branch_name).to_ref_string(),
+ )
+ .expect("not creating cycle");
+ result
+ }
+
+ /// Convenience method to get the target of the `HEAD` reference.
+ /// See also [`SymbolicRefs::head`].
+ pub fn resolve_head(&self) -> Option<&RawTarget> {
+ self.resolve_unprotected(&Unprotected::head())
+ .map(|target| target.as_ref())
+ }
+}
+
+#[derive(Debug, Error)]
+pub enum InsertionError {
+ #[error("inserting symbolic reference '{name} → {target}' would create a cycle")]
+ Cyclic { name: RawName, target: RawTarget },
+
+ #[error(
+ "inserting symbolic reference '{name} → {target}' would result in a symbolic reference targeting an unqualified reference"
+ )]
+ TargetNotQualified { name: RawName, target: RawTarget },
+}
+
+/// Mutability.
+impl SymbolicRefs {
+ /// Insert a symbolic reference.
+ /// Even though this method will never return [`InsertionError::Protected`]
+ /// we opt to return that (slightly more general) error, as it allows
+ /// construction of [`InsertionError::Cyclic`] by consuming `name` and
+ /// `target`, avoiding an early copy in [`Self::try_insert`].
+ pub(super) fn try_insert_unprotected(
+ &mut self,
+ name: Name,
+ target: Target,
+ ) -> Result<(), InsertionError> {
+ if reachable(&self.0, &target, &name) {
+ return Err(InsertionError::Cyclic {
+ name: name.into_inner(),
+ target: target.into_inner(),
+ });
+ }
+
+ let target_is_qualified = Qualified::from_refstr(target.as_ref()).is_some();
+
+ if !target_is_qualified {
+ match self.resolve_unprotected(&target) {
+ Some(end) => {
+ if Qualified::from_refstr(end.as_ref()).is_none() {
+ return Err(InsertionError::TargetNotQualified {
+ name: name.into_inner(),
+ target: target.into_inner(),
+ });
+ }
+ }
+ None => {
+ return Err(InsertionError::TargetNotQualified {
+ name: name.into_inner(),
+ target: target.into_inner(),
+ });
+ }
+ }
+ }
+
+ self.0.insert(name, target);
+ Ok(())
+ }
+
+ /// Try to insert a symbolic reference.
+ /// Errors if `name` or `target` is protected (see [`protect`]) or would
+ /// cause infinite recursion (e.g. `A → B` and `B → A`).
+ ///
+ /// # Panics
+ ///
+ /// If `name` or `target` is not unprotected.
+ #[cfg(test)]
+ fn try_insert(&mut self, name: RawName, target: RawTarget) -> Result<(), InsertionError> {
+ self.try_insert_unprotected(
+ Unprotected::new(name).expect("name is unprotected"),
+ Unprotected::new(target).expect("target is unprotected"),
+ )
+ }
+
+ /// Consume `other` by iteratively inserting into self.
+ pub fn combine(&mut self, other: SymbolicRefs) -> Result<(), InsertionError> {
+ for (name, target) in other.0 {
+ self.try_insert_unprotected(name, target)?;
+ }
+ Ok(())
+ }
+}
+
+#[derive(Debug, Error)]
+#[error("symbolic reference '{name}' is cyclic")]
+pub struct Cyclic {
+ name: RawName,
+}
+
+impl TryFrom<IndexMap<Name, Target>> for SymbolicRefs {
+ type Error = InsertionError;
+
+ fn try_from(map: IndexMap<Name, Target>) -> Result<Self, Self::Error> {
+ let mut result = Self::default();
+ for (name, target) in map.iter() {
+ result.try_insert_unprotected(name.clone(), target.clone())?;
+ }
+ Ok(result)
+ }
+}
+
+mod reachability {
+ pub(super) trait Get<'a, 'b, K, V> {
+ fn get(&'a self, key: &'b K) -> Option<&'a V>;
+ }
+
+ impl<'a, 'b, K: Ord, V> Get<'a, 'b, K, V> for std::collections::BTreeMap<K, V> {
+ fn get(&'a self, key: &'b K) -> Option<&'a V> {
+ std::collections::BTreeMap::get(self, key)
+ }
+ }
+
+ impl<'a, 'b, K: Eq + std::hash::Hash, V> Get<'a, 'b, K, V> for std::collections::HashMap<K, V> {
+ fn get(&'a self, key: &'b K) -> Option<&'a V> {
+ std::collections::HashMap::get(self, key)
+ }
+ }
+
+ impl<'a, 'b, K: Eq + std::hash::Hash, V> Get<'a, 'b, K, V> for indexmap::IndexMap<K, V> {
+ fn get(&'a self, key: &'b K) -> Option<&'a V> {
+ indexmap::IndexMap::get(self, key)
+ }
+ }
+
+ /// A reachability check linking
+ /// from `K` to `V` using [`Get`], and
+ /// from `V` to `K` using [`Into`].
+ /// Note that the bound is trivial if `K = V`.
+ ///
+ /// This can be used to check whether inserting `key → val`
+ /// would create a cycle.
+ ///
+ /// # Returns
+ ///
+ /// Whether `key == val` (under [`Into::into`]) or
+ /// `key` is reachable from `val` (under [`Into::into`] and [`Get::get`]).
+ pub(super) fn reachable<'a, 'b, K: PartialEq, V: 'a>(
+ map: &'a impl Get<'a, 'b, K, V>,
+ val: &'b V,
+ key: &'b K,
+ ) -> bool
+ where
+ 'a: 'b,
+ &'b V: Into<&'b K>,
+ {
+ // Self-Reference
+ let src = val.into();
+ if *src == *key {
+ return true;
+ }
+
+ // Chase
+ let mut src = src;
+ while let Some(tmp) = map.get(src).map(|value| value.into()) {
+ if *tmp == *key {
+ return true;
+ }
+ src = tmp;
+ }
+
+ false
+ }
+}
+
+#[cfg(test)]
+#[allow(clippy::unwrap_used)]
+mod test {
+ use crate::assert_matches;
+ use crate::git::fmt::refname;
+
+ use super::*;
+
+ #[test]
+ fn infinite_single() {
+ let mut symbolic = SymbolicRefs::default();
+
+ assert_matches!(
+ symbolic.try_insert(refname!("a"), refname!("a")),
+ Err(InsertionError::Cyclic { .. })
+ );
+
+ assert!(symbolic.is_empty());
+ }
+
+ #[test]
+ fn infinite_multi() {
+ let mut symbolic = SymbolicRefs::default();
+
+ assert_matches!(
+ symbolic.try_insert(refname!("a"), refname!("refs/heads/b")),
+ Ok(())
+ );
+
+ assert_matches!(
+ symbolic.try_insert(refname!("refs/heads/b"), refname!("refs/heads/c")),
+ Ok(())
+ );
+
+ assert_matches!(
+ symbolic.try_insert(refname!("refs/heads/c"), refname!("a")),
+ Err(InsertionError::Cyclic { .. })
+ );
+ }
+
+ #[test]
+ fn deserialize_valid() {
+ assert_matches!(
+ serde_json::from_value::<SymbolicRefs>(serde_json::json!({
+ "refs/heads/a": "refs/heads/b",
+ })),
+ Ok(_)
+ );
+ }
+
+ #[test]
+ fn deserialize_order() {
+ assert_matches!(
+ serde_json::from_value::<SymbolicRefs>(serde_json::json!({
+ "MAIN": "refs/heads/master",
+ "HEAD": "MAIN",
+ })),
+ Ok(_)
+ );
+
+ assert_matches!(
+ serde_json::from_value::<SymbolicRefs>(serde_json::json!({
+ "HEAD": "MAIN",
+ "MAIN": "refs/heads/master",
+ })),
+ Err(_)
+ );
+ }
+
+ #[test]
+ fn deserialize_infinite() {
+ assert_matches!(
+ serde_json::from_value::<SymbolicRefs>(serde_json::json!({
+ "refs/heads/a": "refs/heads/a",
+ })),
+ Err(_)
+ );
+
+ assert_matches!(
+ serde_json::from_value::<SymbolicRefs>(serde_json::json!({
+ "refs/heads/a": "refs/heads/b",
+ "refs/heads/b": "refs/heads/c",
+ "refs/heads/c": "refs/heads/a",
+ })),
+ Err(_)
+ );
+
+ assert_matches!(
+ serde_json::from_value::<SymbolicRefs>(serde_json::json!({
+ "HEAD": "b",
+ })),
+ Err(_)
+ );
+ }
+
+ /// Motivates why we cannot simply delegate to [`BTreeMap::extend`]
+ /// for combining [`SymbolicRefs`].
+ #[test]
+ fn infinite_extend() {
+ let mut a = SymbolicRefs::default();
+ assert_matches!(
+ a.try_insert(refname!("refs/heads/a"), refname!("refs/heads/b")),
+ Ok(())
+ );
+
+ let mut b = SymbolicRefs::default();
+ assert_matches!(
+ b.try_insert(refname!("refs/heads/b"), refname!("refs/heads/a")),
+ Ok(())
+ );
+
+ assert_matches!(a.combine(b), Err(InsertionError::Cyclic { .. }));
+ }
+}
diff --git a/crates/radicle/src/identity/crefs.rs b/crates/radicle/src/identity/crefs.rs
index 5da76c931..bd65eb4f1 100644
--- a/crates/radicle/src/identity/crefs.rs
+++ b/crates/radicle/src/identity/crefs.rs
@@ -1,22 +1,48 @@
use serde::{Deserialize, Serialize};
+use thiserror::Error;
-use crate::git::canonical::rules::{RawRules, Rules, ValidationError};
+use crate::git::canonical::rules::{self, RawRules, Rules};
+use crate::git::canonical::symbolic::{self, SymbolicRefs};
+use crate::git::fmt::Qualified;
use super::doc::{Delegates, Payload};
+#[derive(Debug, Error)]
+pub enum ValidationError {
+ #[error(transparent)]
+ Rules(#[from] rules::ValidationError),
+
+ #[error("the target of the symbolic reference '{name} → {target}' is not matched by any rule")]
+ Dangling {
+ name: symbolic::RawName,
+ target: symbolic::RawTarget,
+ },
+
+ #[error(
+ "the symbolic reference name '{name}' is also matched by rule(s) with pattern(s) {patterns:?}"
+ )]
+ Clash {
+ patterns: Vec<String>,
+ name: Qualified<'static>,
+ },
+}
+
/// Configuration for canonical references and their rules.
///
-/// `RawCanonicalRefs` are verified into [`CanonicalRefs`].
+/// [`RawCanonicalRefs`] are verified into [`CanonicalRefs`].
#[derive(Default, Debug, Clone, PartialEq, Eq, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RawCanonicalRefs {
rules: RawRules,
+
+ #[serde(default)] // Default to empty for backwards compatibility.
+ symbolic: SymbolicRefs,
}
impl RawCanonicalRefs {
/// Construct a new [`RawCanonicalRefs`] from a set of [`RawRules`].
- pub fn new(rules: RawRules) -> Self {
- Self { rules }
+ pub fn new(rules: RawRules, symbolic: SymbolicRefs) -> Self {
+ Self { rules, symbolic }
}
/// Return the [`RawRules`].
@@ -24,6 +50,21 @@ impl RawCanonicalRefs {
&self.rules
}
+ /// Return the [`RawRules`] for mutation.
+ pub fn raw_rules_mut(&mut self) -> &mut RawRules {
+ &mut self.rules
+ }
+
+ /// Return the [`SymbolicRefs`].
+ pub fn symbolic(&self) -> &SymbolicRefs {
+ &self.symbolic
+ }
+
+ /// Return the [`SymbolicRefs`] for mutation.
+ pub fn symbolic_mut(&mut self) -> &mut SymbolicRefs {
+ &mut self.symbolic
+ }
+
/// Validate the [`RawCanonicalRefs`] into a set of [`CanonicalRefs`].
pub fn try_into_canonical_refs<R>(
self,
@@ -33,7 +74,7 @@ impl RawCanonicalRefs {
R: Fn() -> Delegates,
{
let rules = Rules::from_raw(self.rules, resolve)?;
- Ok(CanonicalRefs::new(rules))
+ CanonicalRefs::new(rules, self.symbolic)
}
}
@@ -41,22 +82,65 @@ impl RawCanonicalRefs {
///
/// [`CanonicalRefs`] can be converted into a [`Payload`] using its [`From`]
/// implementation.
-#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
+#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CanonicalRefs {
rules: Rules,
+
+ #[serde(default, skip_serializing_if = "SymbolicRefs::is_empty")]
+ symbolic: SymbolicRefs,
}
impl CanonicalRefs {
- /// Construct a new [`CanonicalRefs`] from a set of [`Rules`].
- pub fn new(rules: Rules) -> Self {
- CanonicalRefs { rules }
+ /// Construct a new [`CanonicalRefs`] from a set of [`Rules`] and
+ /// [`SymbolicRefs`], validating that these may be evaluated to a well
+ /// formed set of references when interpreted together.
+ pub fn new(rules: Rules, symbolic: SymbolicRefs) -> Result<Self, ValidationError> {
+ for (name, target) in symbolic.iter_resolved() {
+ if Qualified::from_refstr(target)
+ .and_then(|qualified| rules.matches(&qualified).next())
+ .is_none()
+ {
+ return Err(ValidationError::Dangling {
+ name: name.to_owned(),
+ target: target.to_owned(),
+ });
+ }
+
+ let Some(name) = Qualified::from_refstr(name) else {
+ continue;
+ };
+
+ let patterns = rules
+ .matches(&name)
+ .map(|(pattern, _)| pattern.to_string())
+ .collect::<Vec<_>>();
+ if !patterns.is_empty() {
+ return Err(ValidationError::Clash {
+ patterns,
+ name: name.to_owned(),
+ });
+ }
+ }
+
+ Ok(CanonicalRefs { rules, symbolic })
}
/// Return the [`Rules`].
pub fn rules(&self) -> &Rules {
&self.rules
}
+
+ /// Return the [`SymbolicRefs`].
+ pub fn symbolic(&self) -> &SymbolicRefs {
+ &self.symbolic
+ }
+}
+
+impl Extend<(rules::RawPattern, rules::RawRule)> for RawCanonicalRefs {
+ fn extend<T: IntoIterator<Item = (rules::RawPattern, rules::RawRule)>>(&mut self, iter: T) {
+ self.rules.extend(iter)
+ }
}
#[derive(Debug, thiserror::Error)]
@@ -74,3 +158,120 @@ impl TryFrom<CanonicalRefs> for Payload {
Ok(Self::from(value))
}
}
+
+#[cfg(test)]
+#[allow(clippy::unwrap_used)]
+mod tests {
+ use serde_json::json;
+
+ use crate::assert_matches;
+
+ use super::{ValidationError::*, *};
+
+ fn from(value: serde_json::Value) -> Result<CanonicalRefs, super::ValidationError> {
+ let delegates: Delegates = crate::test::arbitrary::r#gen::<crate::prelude::Did>(1).into();
+ serde_json::from_value::<RawCanonicalRefs>(value)
+ .unwrap()
+ .try_into_canonical_refs(&mut || delegates.clone())
+ }
+
+ /// Backwards compatibility to before addition of symbolic references.
+ #[test]
+ fn omit_symbolic() {
+ assert_matches!(
+ from(json!({
+ "rules": {},
+ })),
+ Ok(_)
+ );
+ }
+
+ #[test]
+ fn invalid_dangling() {
+ assert_matches!(
+ from(json!({
+ "symbolic": {
+ "HEAD": "refs/heads/master"
+ },
+ "rules": {},
+ })),
+ Err(Dangling { .. })
+ );
+ }
+
+ #[test]
+ fn invalid_clash() {
+ assert_matches!(
+ from(json!({
+ "symbolic": {
+ "refs/heads/foo": "refs/heads/bar",
+ },
+ "rules": {
+ "refs/heads/foo": {
+ "allow": "delegates",
+ "threshold": 1,
+ },
+ "refs/heads/bar": {
+ "allow": "delegates",
+ "threshold": 1,
+ },
+ },
+ })),
+ Err(Clash { .. })
+ );
+ }
+
+ #[test]
+ fn invalid_clash_asterisk_name() {
+ assert_matches!(
+ from(json!({
+ "symbolic": {
+ "refs/heads/foo": "refs/heads/bar",
+ },
+ "rules": {
+ "refs/heads/*": {
+ "allow": "delegates",
+ "threshold": 1,
+ },
+ },
+ })),
+ Err(Clash { .. })
+ );
+ }
+
+ #[test]
+ fn valid_asterisk_target() {
+ assert_matches!(
+ from(json!({
+ "symbolic": {
+ "HEAD": "refs/heads/master",
+ },
+ "rules": {
+ "refs/heads/*": {
+ "allow": "delegates",
+ "threshold": 1,
+ },
+ },
+ })),
+ Ok(_)
+ );
+ }
+
+ #[test]
+ fn valid() {
+ assert_matches!(
+ from(json!({
+ "symbolic": {
+ "refs/heads/foo": "refs/heads/ruled/bar",
+ },
+ "rules": {
+ "refs/heads/ruled/*": {
+ "allow": "delegates",
+ "threshold": 1,
+ },
+ },
+ })),
+ Ok(_)
+ );
+ }
+}
diff --git a/crates/radicle/src/identity/doc.rs b/crates/radicle/src/identity/doc.rs
index 51dc204f0..0bc64c15d 100644
--- a/crates/radicle/src/identity/doc.rs
+++ b/crates/radicle/src/identity/doc.rs
@@ -20,7 +20,11 @@ use crate::crypto;
use crate::crypto::Signature;
use crate::git;
use crate::git::canonical::rules;
+use crate::git::canonical::symbolic;
+use crate::git::fmt::Qualified;
+use crate::git::fmt::RefString;
use crate::git::raw::ErrorExt as _;
+use crate::identity::crefs;
use crate::identity::{Did, project::Project};
use crate::node::device::Device;
use crate::storage;
@@ -75,9 +79,25 @@ impl DocError {
}
#[derive(Debug, Error)]
-pub enum DefaultBranchRuleError {
- #[error("could not load `xyz.radicle.project` to get default branch name: {0}")]
- Payload(#[from] PayloadError),
+pub enum DefaultBranchError {
+ // #[error("could not load `xyz.radicle.project` to get default branch name: {0}")]
+ // Payload(#[from] PayloadError),
+ #[error(
+ "could not find default branch in any of the payloads `xyz.radicle.project` ({project}) or `xyz.radicle.crefs` ({crefs})"
+ )]
+ Payload {
+ project: PayloadError,
+ crefs: PayloadError,
+ },
+
+ #[error("no symbolic reference with name `HEAD` is defined")]
+ MissingHead,
+
+ #[error(transparent)]
+ CanonicalRefsError(#[from] CanonicalRefsError),
+
+ #[error("the target of the symbolic reference `HEAD` is not qualified: {0}")]
+ UnqualifiedHead(git::fmt::RefString),
}
/// The version number of the identity document.
@@ -757,42 +777,137 @@ impl Doc {
}
/// Gets the qualified reference name of the default branch,
- /// according to the project payload in this document.
- pub fn default_branch(&self) -> Result<git::fmt::Qualified<'_>, PayloadError> {
- Ok(git::refs::branch(self.project()?.default_branch()))
- }
-
- pub fn default_branch_rule(&self) -> Result<rules::Rules, DefaultBranchRuleError> {
- let pattern = git::fmt::refspec::QualifiedPattern::from(git::refs::branch(
- self.project()?.default_branch(),
- ));
- let rule = rules::Rule::new(
- rules::ResolvedDelegates::Delegates(self.delegates.clone()),
- self.threshold,
- );
- Ok(rules::Rules::from_raw(
- rules::RawRules::from_iter([(pattern, rule.into())]),
- &mut || self.delegates.clone(),
- )
- .expect("default rules are valid"))
+ /// according to payloads `xyz.radicle.project` and `xyz.radicle.crefs`
+ /// in this document.
+ pub fn default_branch(&self) -> Result<git::fmt::Qualified<'_>, DefaultBranchError> {
+ let crefs = self.canonical_refs()?;
+ let refname = crefs
+ .symbolic()
+ .resolve_head()
+ .ok_or(DefaultBranchError::MissingHead)?;
+ let qualified = refname
+ .qualified()
+ .ok_or(DefaultBranchError::UnqualifiedHead(refname.clone()))?;
+ Ok(qualified.to_owned())
}
/// Construct the canonical references for this document.
/// The implementation of [`RawCanonicalRefs`] is used to
/// obtain the payload identified by [`PayloadId::canonical_refs`], if it
/// exists.
- /// The resulting [`CanonicalRefs`] are constructed by extension with
- /// [`Self::default_branch_rule`].
///
/// [`RawCanonicalRefs`]: super::crefs::RawCanonicalRefs
+ ///
+ /// Starts by obtaining the payload identified by
+ /// [`PayloadId::canonical_refs`].
+ ///
+ /// If the payload exists, and it contains both a symbolic reference with
+ /// the name `HEAD` and a rule matching the corresponding target branch,
+ /// then this rule is verified to be backwards compatible, i.e. that the
+ /// value for `allowed` is [`rules::Allowed::Delegates`] and the threshold
+ /// matches [`Self::threshold`]. If additionally a payload identified by
+ /// [`PayloadId::project`] exists and can be loaded, then the target
+ /// branch of the symbolic reference with name `HEAD` is verified to match
+ /// [`Project::default_branch_qualified`] as well.
+ ///
+ /// If the payload is missing, canonical references are synthesized from
+ /// the payload identified by [`PayloadId::project`]:
+ /// - A rule exactly matching [`Project::default_branch`]
+ /// that is compatible with self.
+ /// - A symbolic reference with name `HEAD`
+ /// (see [`symbolic::SymbolicRefs::head`]) that targets the same branch.
+ ///
+ /// The resulting [`CanonicalRefs`] must pass validation, and there are
+ /// cases where the payload is valid as such, but invalid in combination
+ /// with the synthesized rule and symbolic reference. For example, if
+ /// there is a symbolic reference already, with the name of the default
+ /// branch, this will clash with the synthesized rule.
pub fn canonical_refs(&self) -> Result<CanonicalRefs, CanonicalRefsError> {
+ let project = self.project();
+
let raw_crefs = self.raw_canonical_refs()?.unwrap_or_default();
- let mut raw_rules = raw_crefs.raw_rules().clone();
- raw_rules.extend(rules::RawRules::from(self.default_branch_rule()?));
+ let resolve = &mut || self.delegates.clone();
+
+ // If there is a symbolic reference with name `HEAD` in the crefs
+ // payload, we do not need to access the project payload to obtain
+ // the name of the default branch from there.
+ // However, we must still ensure that the crefs payload, in particular
+ // the rule matching the target branch of the symbolic reference with
+ // name `HEAD`, is backwards compatible with the rest of the identity
+ // document.
+ // These conditions may only be relaxed by introducing a new version of
+ // the identity document.
+ if let Some(default_branch) = raw_crefs.symbolic().resolve_head() {
+ if let Ok(project) = &project {
+ let project = project.default_branch_qualified().to_ref_string();
+ if project != *default_branch {
+ return Err(CanonicalRefsError::DefaultBranchRuleError(
+ DefaultBranchRuleError::HeadMismatch {
+ cref: default_branch.clone(),
+ project,
+ },
+ ));
+ }
+ }
- let raw_crefs = RawCanonicalRefs::new(raw_rules);
- Ok(raw_crefs.try_into_canonical_refs(&mut || self.delegates.clone())?)
+ if let Some(default_branch) = Qualified::from_refstr(default_branch)
+ && let Some((pattern, rule)) = raw_crefs.raw_rules().matches(&default_branch).next()
+ {
+ if *rule.allowed() != rules::Allowed::Delegates {
+ return Err(CanonicalRefsError::DefaultBranchRuleError(
+ DefaultBranchRuleError::Allowed {
+ pattern: pattern.to_string(),
+ actual: rule.allowed().to_string(),
+ },
+ ));
+ }
+ if *rule.threshold() != self.threshold() {
+ return Err(CanonicalRefsError::DefaultBranchRuleError(
+ DefaultBranchRuleError::Threshold {
+ pattern: pattern.to_string(),
+ actual: *rule.threshold(),
+ expected: self.threshold(),
+ },
+ ));
+ }
+ // There is a symbolic reference for `HEAD`, but no matching
+ // canonical reference rule. `HEAD` is dangling!
+ // `raw_crefs` is malformed and will not pass validation below.
+ }
+ return Ok(raw_crefs.try_into_canonical_refs(resolve)?);
+ }
+
+ // Since there is no symbolic reference with name `HEAD`, fall back
+ // to the project payload for obtaining the default branch.
+ let project = project.map_err(CanonicalRefsError::SynthesisPayloadMissing)?;
+
+ // Only now, once it is known that synthesis will be required,
+ // and have a project to do so, make `raw_crefs` mutable.
+ let mut raw_crefs = raw_crefs;
+
+ let default_branch = project.default_branch_qualified();
+
+ if raw_crefs
+ .raw_rules()
+ .matches(&default_branch)
+ .next()
+ .is_none()
+ {
+ let rule = rules::Rule::new(rules::Allowed::Delegates, self.threshold());
+
+ raw_crefs.raw_rules_mut().insert(
+ git::fmt::refspec::QualifiedPattern::from(default_branch.to_owned()),
+ rule,
+ );
+ }
+
+ raw_crefs
+ .symbolic_mut()
+ .combine(symbolic::SymbolicRefs::head(project.default_branch()))
+ .map_err(|source| CanonicalRefsError::SynthesisCycle { source })?;
+
+ Ok(raw_crefs.try_into_canonical_refs(resolve)?)
}
/// Return the associated [`Visibility`] of this document.
@@ -952,20 +1067,50 @@ impl Doc {
}
}
+#[derive(Debug, Error)]
+pub enum RawCanonicalRefsError {
+ #[error(transparent)]
+ Json(#[from] serde_json::Error),
+}
+
#[derive(Debug, Error)]
pub enum CanonicalRefsError {
#[error(transparent)]
Raw(#[from] RawCanonicalRefsError),
+
#[error(transparent)]
- CanonicalRefs(#[from] rules::ValidationError),
+ CanonicalRefs(#[from] crefs::ValidationError),
+
+ #[error("could not load `xyz.radicle.project` to get default branch name: {0}")]
+ SynthesisPayloadMissing(PayloadError),
+
#[error(transparent)]
- DefaultBranch(#[from] DefaultBranchRuleError),
+ DefaultBranchRuleError(#[from] DefaultBranchRuleError),
+
+ #[error("synthesizing canonical references from project payload failed: {source}")]
+ SynthesisCycle { source: symbolic::InsertionError },
}
#[derive(Debug, Error)]
-pub enum RawCanonicalRefsError {
- #[error(transparent)]
- Json(#[from] serde_json::Error),
+pub enum DefaultBranchRuleError {
+ #[error(
+ "rule for pattern '{pattern}' which matches the target of symbolic reference 'HEAD' (possibly synthesized from payload 'xyz.radicle.project') must use 'allow' value of \"delegates\" but uses {actual}"
+ )]
+ Allowed { pattern: String, actual: String },
+
+ #[error(
+ "rule for pattern '{pattern}' which matches the target of symbolic reference 'HEAD' (possibly synthesized from payload 'xyz.radicle.project') must use a threshold of {expected} as required by the identity document but uses {actual}"
+ )]
+ Threshold {
+ pattern: String,
+ actual: usize,
+ expected: usize,
+ },
+
+ #[error(
+ "target symbolic reference 'HEAD' ('{cref}') does not match `defaultBranch` from payload `xyz.radicle.project` ('{project}')"
+ )]
+ HeadMismatch { cref: RefString, project: RefString },
}
pub trait GetRawCanonicalRefs: GetPayload {
@@ -1232,4 +1377,72 @@ mod test {
serde_json::json!({ "type": "private", "allow": ["did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT"] })
);
}
+
+ #[test]
+ fn default_branch_without_project() {
+ let value = serde_json::json!(
+ {
+ "payload": {
+ "xyz.radicle.crefs": {
+ "symbolic": {
+ "HEAD": "refs/heads/main",
+ },
+ "rules": {
+ "refs/heads/main": {
+ "allow": "delegates",
+ "threshold": 1,
+ }
+ }
+ }
+ },
+ "delegates": [
+ "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM"
+ ],
+ "threshold": 1
+ }
+ );
+
+ let doc = serde_json::from_value::<Doc>(value).unwrap();
+ assert_eq!(doc.default_branch().unwrap().as_str(), "refs/heads/main");
+ }
+
+ #[test]
+ fn default_branch_clash() {
+ let value = serde_json::json!(
+ {
+ "payload": {
+ "xyz.radicle.project": {
+ "name": "example",
+ "description": "An example project",
+ "defaultBranch": "main",
+ },
+ "xyz.radicle.crefs": {
+ "symbolic": {
+ "HEAD": "refs/heads/master",
+ },
+ "rules": {
+ "refs/heads/master": {
+ "allow": "delegates",
+ "threshold": 1,
+ }
+ }
+ }
+ },
+ "delegates": [
+ "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM"
+ ],
+ "threshold": 1
+ }
+ );
+
+ let doc = serde_json::from_value::<Doc>(value).unwrap();
+ assert_matches!(
+ doc.default_branch(),
+ Err(DefaultBranchError::CanonicalRefsError(
+ CanonicalRefsError::DefaultBranchRuleError(
+ DefaultBranchRuleError::HeadMismatch { .. }
+ )
+ ))
+ );
+ }
}
diff --git a/crates/radicle/src/identity/doc/update.rs b/crates/radicle/src/identity/doc/update.rs
index fbec64572..3ea587232 100644
--- a/crates/radicle/src/identity/doc/update.rs
+++ b/crates/radicle/src/identity/doc/update.rs
@@ -208,9 +208,13 @@ pub fn verify(raw: RawDoc) -> Result<Doc, error::DocVerification> {
});
}
};
- // Ensure that if we have canonical reference rules and a project, that no
- // rule exists for the default branch. This rule must be synthesized when
- // constructing the canonical reference rules.
+
+ // If we have both payloads `xyz.radicle.{project,crefs}` ensure that,
+ // in the `crefs` payload there is no …
+ // 1. … rule that matches the default branch from the `project` payload.
+ // (This rule must be synthesized!)
+ // 2. … symbolic reference with the name `HEAD`.
+ // (This reference must be synthesized!)
use super::GetRawCanonicalRefs as _;
match raw
.raw_canonical_refs()
@@ -225,11 +229,19 @@ pub fn verify(raw: RawDoc) -> Result<Doc, error::DocVerification> {
.map(|(pattern, _)| pattern.to_string())
.collect::<Vec<_>>();
if !matches.is_empty() {
- return Err(error::DocVerification::DisallowDefault { matches, default });
+ return Err(error::DocVerification::DisallowDefaultBranchRule { matches, default });
+ }
+
+ if let Some(symbolic) = crefs.symbolic().resolve_head() {
+ return Err(error::DocVerification::DisallowDefaultBranchSymbolic {
+ symbolic: symbolic.to_owned(),
+ default,
+ });
}
}
_ => { /* we validate below */ }
}
+
// Verify that the canonical references payload is valid
if let Err(e) = proposal.canonical_refs() {
return Err(error::DocVerification::PayloadError {
@@ -332,7 +344,7 @@ mod test {
assert!(
matches!(
super::verify(raw),
- Err(error::DocVerification::DisallowDefault { .. })
+ Err(error::DocVerification::DisallowDefaultBranchRule { .. })
),
"Verification should be rejected for including default branch rule"
)
diff --git a/crates/radicle/src/identity/doc/update/error.rs b/crates/radicle/src/identity/doc/update/error.rs
index bf19aca66..31ffde52a 100644
--- a/crates/radicle/src/identity/doc/update/error.rs
+++ b/crates/radicle/src/identity/doc/update/error.rs
@@ -31,10 +31,17 @@ pub enum DocVerification {
#[error(
"incompatible payloads: The rule(s) xyz.radicle.crefs.rules.{matches:?} matches the value of xyz.radicle.project.defaultBranch ('{default}'). Possible resolutions: Change the name of the default branch or remove the rule(s)."
)]
- DisallowDefault {
+ DisallowDefaultBranchRule {
matches: Vec<String>,
default: git::fmt::Qualified<'static>,
},
+ #[error(
+ "incompatible payloads: The symbolic reference xyz.radicle.crefs.symbolic.HEAD → '{symbolic}' conflicts with xyz.radicle.project.defaultBranch ('{default}'). Possible resolutions: Remove either of the two."
+ )]
+ DisallowDefaultBranchSymbolic {
+ symbolic: RefString,
+ default: git::fmt::Qualified<'static>,
+ },
}
#[derive(Clone, Debug)]
diff --git a/crates/radicle/src/identity/project.rs b/crates/radicle/src/identity/project.rs
index 9f2504753..6509e7de7 100644
--- a/crates/radicle/src/identity/project.rs
+++ b/crates/radicle/src/identity/project.rs
@@ -8,6 +8,7 @@ use thiserror::Error;
use crate::crypto;
use crate::git::BranchName;
+use crate::git::{fmt::Qualified, refs::branch};
use crate::identity::doc;
use crate::identity::doc::Payload;
@@ -254,6 +255,12 @@ impl Project {
pub fn default_branch(&self) -> &BranchName {
&self.default_branch
}
+
+ /// Return the qualified name of the default branch.
+ #[inline]
+ pub fn default_branch_qualified(&self) -> Qualified<'_> {
+ branch(&self.default_branch)
+ }
}
impl From<Project> for Payload {
diff --git a/crates/radicle/src/rad.rs b/crates/radicle/src/rad.rs
index 1d343cafd..c6fe38f3a 100644
--- a/crates/radicle/src/rad.rs
+++ b/crates/radicle/src/rad.rs
@@ -143,7 +143,7 @@ where
)?;
stored.set_remote_identity_root_to(pk, identity)?;
stored.set_identity_head_to(identity)?;
- stored.set_head_to_default_branch()?;
+ stored.set_canonical_symbolic_refs("set-canonical from init (radicle)")?;
stored.set_default_branch_to_canonical_head()?;
let signed = stored.sign_refs(signer)?;
diff --git a/crates/radicle/src/storage.rs b/crates/radicle/src/storage.rs
index f407cd7dc..d54c3f17f 100644
--- a/crates/radicle/src/storage.rs
+++ b/crates/radicle/src/storage.rs
@@ -150,7 +150,7 @@ pub enum RepositoryError {
#[error("missing canonical reference rule for default branch")]
MissingBranchRule,
#[error("could not get the default branch rule: {0}")]
- DefaultBranchRule(#[from] doc::DefaultBranchRuleError),
+ DefaultBranchRule(#[from] doc::DefaultBranchError),
#[error("failed to get canonical reference rules: {0}")]
CanonicalRefs(#[from] doc::CanonicalRefsError),
#[error(transparent)]
@@ -668,11 +668,27 @@ where
/// Allows read-write access to a repository.
pub trait WriteRepository: ReadRepository + SignRepository {
- /// Sets the symbolic reference `HEAD` to target the default branch.
- /// This only depends on the value for the default branch in the identity
- /// document, and does not require the canonical reference behind the
- /// default branch to be computed, or even exist.
- fn set_head_to_default_branch(&self) -> Result<(), RepositoryError>;
+ /// Sets the canonical symbolic references.
+ ///
+ /// This only depends on canonical references (thus the `xyz.radicle.crefs`
+ /// payload, and possibly the `xyz.radicle.project` payload in the identity
+ /// document). The targeted canonical references are not computed and might
+ /// not even exist.
+ fn set_canonical_symbolic_refs(&self, message: &str) -> Result<(), RepositoryError> {
+ for (name, target) in self.identity_doc()?.canonical_refs()?.symbolic().iter() {
+ self.set_symbolic_ref(name, target, message)?;
+ }
+ Ok(())
+ }
+
+ /// Sets a symbolic reference, if it does not exist or its target is different
+ /// from the given one.
+ fn set_symbolic_ref(
+ &self,
+ name: &RefStr,
+ target: &RefStr,
+ message: &str,
+ ) -> Result<(), RepositoryError>;
/// Computes the head of the default branch based on the delegate set,
/// and sets it.
diff --git a/crates/radicle/src/storage/git.rs b/crates/radicle/src/storage/git.rs
index d3107a98a..c70efca4b 100644
--- a/crates/radicle/src/storage/git.rs
+++ b/crates/radicle/src/storage/git.rs
@@ -28,7 +28,7 @@ use crate::{git, git::Oid, node};
use crate::git::RefError;
use crate::git::UserInfo;
use crate::git::fmt::{
- Qualified, RefString, refname, refspec, refspec::PatternStr, refspec::PatternString,
+ Qualified, RefStr, RefString, refspec, refspec::PatternStr, refspec::PatternString,
};
pub use crate::storage::{Error, RepositoryError};
@@ -852,20 +852,17 @@ impl ReadRepository for Repository {
fn canonical_head(&self) -> Result<(Qualified<'_>, Oid), RepositoryError> {
let doc = self.identity_doc()?;
- let refname = doc.default_branch()?.to_owned();
-
- let crefs = doc.canonical_refs()?;
-
- Ok(crefs
+ Ok(doc
+ .canonical_refs()?
.rules()
- .canonical(refname, self)
+ .canonical(doc.default_branch()?, self)
.ok_or(RepositoryError::MissingBranchRule)?
.find_objects()?
.quorum()?)
.map(
|Quorum {
refname, object, ..
- }| (refname, object.id()),
+ }| (refname.to_owned(), object.id()),
)
}
@@ -939,31 +936,28 @@ impl ReadRepository for Repository {
}
impl WriteRepository for Repository {
- fn set_head_to_default_branch(&self) -> Result<(), RepositoryError> {
- let head_ref = refname!("HEAD");
- let branch_ref = self.default_branch()?;
-
- match self.raw().find_reference(head_ref.as_str()) {
+ fn set_symbolic_ref(
+ &self,
+ name: &RefStr,
+ target: &RefStr,
+ message: &str,
+ ) -> Result<(), RepositoryError> {
+ match self.raw().find_reference(name.as_str()) {
Ok(mut head_ref) => {
if head_ref
.symbolic_target()
- .is_some_and(|t| t != branch_ref.as_str())
+ .is_some_and(|t| t != target.as_str())
{
- head_ref.symbolic_set_target(branch_ref.as_str(), "set-head (radicle)")?;
+ head_ref.symbolic_set_target(target.as_str(), message)?;
}
- Ok(())
}
Err(err) if err.is_not_found() => {
- self.raw().reference_symbolic(
- head_ref.as_str(),
- branch_ref.as_str(),
- true,
- "set-head (radicle)",
- )?;
- Ok(())
+ self.raw()
+ .reference_symbolic(name.as_str(), target.as_str(), true, message)?;
}
- Err(err) => Err(err.into()),
+ Err(err) => return Err(err.into()),
}
+ Ok(())
}
fn set_default_branch_to_canonical_head(&self) -> Result<SetHead, RepositoryError> {
diff --git a/crates/radicle/src/test.rs b/crates/radicle/src/test.rs
index 9ca677ce9..89b362897 100644
--- a/crates/radicle/src/test.rs
+++ b/crates/radicle/src/test.rs
@@ -59,7 +59,7 @@ pub fn fetch<W: WriteRepository>(
drop(opts);
repo.set_identity_head()?;
- repo.set_head_to_default_branch()?;
+ repo.set_canonical_symbolic_refs("set-canonical test (radicle)")?;
repo.set_default_branch_to_canonical_head()?;
let validations = repo.validate()?;
diff --git a/crates/radicle/src/test/storage.rs b/crates/radicle/src/test/storage.rs
index c32881af2..5fefd63b7 100644
--- a/crates/radicle/src/test/storage.rs
+++ b/crates/radicle/src/test/storage.rs
@@ -344,7 +344,12 @@ impl WriteRepository for MockRepository {
todo!()
}
- fn set_head_to_default_branch(&self) -> Result<(), RepositoryError> {
+ fn set_symbolic_ref(
+ &self,
+ _name: &fmt::RefStr,
+ _target: &fmt::RefStr,
+ _message: &str,
+ ) -> Result<(), RepositoryError> {
todo!()
}
Exit code: 0
shell: 'export RUSTDOCFLAGS=''-D warnings'' cargo --version rustc --version cargo fmt --check cargo clippy --all-targets --workspace -- --deny warnings cargo build --all-targets --workspace cargo doc --workspace --no-deps --all-features cargo test --workspace --no-fail-fast '
Commands:
$ podman run --name 7cd05d0d-27df-4be6-a215-8460a6240a7c -v /opt/radcis/ci.rad.levitte.org/cci/state/7cd05d0d-27df-4be6-a215-8460a6240a7c/s:/7cd05d0d-27df-4be6-a215-8460a6240a7c/s:ro -v /opt/radcis/ci.rad.levitte.org/cci/state/7cd05d0d-27df-4be6-a215-8460a6240a7c/w:/7cd05d0d-27df-4be6-a215-8460a6240a7c/w -w /7cd05d0d-27df-4be6-a215-8460a6240a7c/w -v /opt/radcis/ci.rad.levitte.org/.radicle:/${id}/.radicle:ro -e RAD_HOME=/${id}/.radicle rust:trixie bash /7cd05d0d-27df-4be6-a215-8460a6240a7c/s/script.sh
+ export 'RUSTDOCFLAGS=-D warnings'
+ RUSTDOCFLAGS='-D warnings'
+ cargo --version
info: syncing channel updates for '1.90-x86_64-unknown-linux-gnu'
info: latest update on 2025-09-18, rust version 1.90.0 (1159e78c4 2025-09-14)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
info: downloading component 'rust-src'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
info: installing component 'rust-src'
info: installing component 'rust-std'
info: installing component 'rustc'
info: installing component 'rustfmt'
cargo 1.90.0 (840b83a10 2025-07-30)
+ rustc --version
rustc 1.90.0 (1159e78c4 2025-09-14)
+ cargo fmt --check
+ cargo clippy --all-targets --workspace -- --deny warnings
Updating crates.io index
Downloading crates ...
Downloaded base-x v0.2.11
Downloaded ascii v1.1.0
Downloaded ahash v0.8.12
Downloaded form_urlencoded v1.2.2
Downloaded gix-tempfile v21.0.1
Downloaded find-msvc-tools v0.1.9
Downloaded console v0.16.3
Downloaded filetime v0.2.27
Downloaded document-features v0.2.12
Downloaded bit-set v0.8.0
Downloaded bcrypt-pbkdf v0.10.0
Downloaded crypto-common v0.1.7
Downloaded data-encoding-macro-internal v0.1.17
Downloaded gix-hash v0.22.1
Downloaded gix-negotiate v0.27.0
Downloaded ed25519-dalek v2.2.0
Downloaded gix-trace v0.1.18
Downloaded humantime v2.3.0
Downloaded itoa v1.0.17
Downloaded git-ref-format-core v0.6.0
Downloaded keccak v0.1.6
Downloaded gix-traverse v0.52.0
Downloaded heapless v0.8.0
Downloaded heck v0.5.0
Downloaded gix-utils v0.3.1
Downloaded inout v0.1.4
Downloaded gix-packetline v0.21.1
Downloaded gix-transport v0.54.0
Downloaded num-cmp v0.1.0
Downloaded elliptic-curve v0.13.8
Downloaded ppv-lite86 v0.2.21
Downloaded nonempty v0.9.0
Downloaded gix-sec v0.13.1
Downloaded lock_api v0.4.14
Downloaded num v0.4.3
Downloaded multibase v0.9.2
Downloaded ref-cast-impl v1.0.25
Downloaded pastey v0.2.1
Downloaded quick-error v1.2.3
Downloaded potential_utf v0.1.4
Downloaded percent-encoding v2.3.2
Downloaded log v0.4.29
Downloaded rand_xorshift v0.4.0
Downloaded rand_chacha v0.3.1
Downloaded rustc_version v0.4.1
Downloaded poly1305 v0.8.0
Downloaded secrecy v0.10.3
Downloaded nu-ansi-term v0.50.3
Downloaded parking_lot v0.12.5
Downloaded signature v2.2.0
Downloaded maybe-async v0.2.10
Downloaded rfc6979 v0.4.0
Downloaded p256 v0.13.2
Downloaded sha1 v0.10.6
Downloaded signal-hook-mio v0.2.5
Downloaded schemars_derive v1.2.1
Downloaded snapbox-macros v0.3.10
Downloaded ssh-encoding v0.2.0
Downloaded serde_fmt v1.1.0
Downloaded serde_spanned v1.0.4
Downloaded sval_json v2.17.0
Downloaded stable_deref_trait v1.2.1
Downloaded serde-untagged v0.1.9
Downloaded test-log-macros v0.2.19
Downloaded test-log v0.2.19
Downloaded rusty-fork v0.3.1
Downloaded shlex v1.3.0
Downloaded serde_derive_internals v0.29.1
Downloaded sha2 v0.10.9
Downloaded sval_serde v2.17.0
Downloaded tinyvec_macros v0.1.1
Downloaded signal-hook-registry v1.4.8
Downloaded tree-sitter-language v0.1.7
Downloaded unarray v0.1.4
Downloaded thiserror v1.0.69
Downloaded value-bag-sval2 v1.12.0
Downloaded version_check v0.9.5
Downloaded typeid v1.0.3
Downloaded similar v2.7.0
Downloaded yoke-derive v0.8.1
Downloaded ryu v1.0.23
Downloaded zerofrom v0.1.6
Downloaded thiserror-impl v2.0.18
Downloaded vsimd v0.8.0
Downloaded xattr v1.6.1
Downloaded toml v0.9.12+spec-1.1.0
Downloaded zerovec-derive v0.11.2
Downloaded zerofrom-derive v0.1.6
Downloaded spin v0.9.8
Downloaded universal-hash v0.5.1
Downloaded radicle-surf v0.27.1
Downloaded walkdir v2.5.0
Downloaded fancy-regex v0.14.0
Downloaded p521 v0.13.3
Downloaded writeable v0.6.2
Downloaded typenum v1.19.0
Downloaded zmij v1.0.21
Downloaded value-bag v1.12.0
Downloaded tree-sitter-css v0.23.2
Downloaded rand v0.8.5
Downloaded zerotrie v0.2.3
Downloaded regex v1.12.3
Downloaded uuid v1.22.0
Downloaded unicode-ident v1.0.24
Downloaded tar v0.4.45
Downloaded socket2 v0.5.10
Downloaded yansi v1.0.1
Downloaded portable-atomic v1.13.1
Downloaded sval v2.17.0
Downloaded zerovec v0.11.5
Downloaded url v2.5.8
Downloaded tree-sitter-go v0.23.4
Downloaded unicode-normalization v0.1.25
Downloaded icu_properties_data v2.1.2
Downloaded tracing-subscriber v0.3.23
Downloaded serde_json v1.0.149
Downloaded zlib-rs v0.6.3
Downloaded unicode-width v0.2.2
Downloaded syn v2.0.117
Downloaded zerocopy v0.8.42
Downloaded gimli v0.32.3
Downloaded tree-sitter-md v0.3.2
Downloaded tree-sitter-rust v0.23.3
Downloaded regex-syntax v0.8.10
Downloaded tree-sitter-c v0.23.4
Downloaded sysinfo v0.37.2
Downloaded proptest v1.10.0
Downloaded tree-sitter-bash v0.23.3
Downloaded tracing v0.1.44
Downloaded rustix v1.1.4
Downloaded object v0.37.3
Downloaded git2 v0.20.4
Downloaded tree-sitter-ruby v0.23.1
Downloaded winnow v0.7.15
Downloaded regex-automata v0.4.14
Downloaded vcpkg v0.2.15
Downloaded syn v1.0.109
Downloaded bstr v1.12.1
Downloaded curve25519-dalek v4.1.3
Downloaded libc v0.2.183
Downloaded tree-sitter v0.24.7
Downloaded ssh-key v0.6.7
Downloaded tree-sitter-python v0.23.6
Downloaded libz-sys v1.1.25
Downloaded idna v1.1.0
Downloaded sha1-checked v0.10.0
Downloaded jiff v0.2.23
Downloaded jsonschema v0.30.0
Downloaded itertools v0.14.0
Downloaded tree-sitter-typescript v0.23.2
Downloaded tokio v1.50.0
Downloaded hashbrown v0.16.1
Downloaded sha3 v0.10.8
Downloaded num-bigint v0.4.6
Downloaded rand v0.9.2
Downloaded serde v1.0.228
Downloaded indexmap v2.13.0
Downloaded bloomy v1.2.0
Downloaded p384 v0.13.1
Downloaded flate2 v1.1.9
Downloaded unicode-segmentation v1.12.0
Downloaded prodash v31.0.0
Downloaded mio v1.1.1
Downloaded zeroize v1.8.2
Downloaded tempfile v3.27.0
Downloaded ssh-agent-lib v0.5.2
Downloaded jiff-static v0.2.23
Downloaded sharded-slab v0.1.7
Downloaded inquire v0.9.4
Downloaded chrono v0.4.44
Downloaded tracing-core v0.1.36
Downloaded thiserror-impl v1.0.69
Downloaded sval_buffer v2.17.0
Downloaded tree-sitter-highlight v0.24.7
Downloaded snapbox v0.4.17
Downloaded gix-diff v0.58.0
Downloaded libm v0.2.16
Downloaded yoke v0.8.1
Downloaded tinyvec v1.11.0
Downloaded systemd-journal-logger v2.2.2
Downloaded signal-hook v0.3.18
Downloaded schemars v1.2.1
Downloaded timeago v0.4.2
Downloaded rsa v0.9.10
Downloaded miniz_oxide v0.8.9
Downloaded signals_receipts v0.2.5
Downloaded tinystr v0.8.2
Downloaded thread_local v1.1.9
Downloaded sval_nested v2.17.0
Downloaded clap_builder v4.6.0
Downloaded tracing-log v0.2.0
Downloaded toml_writer v1.0.7+spec-1.1.0
Downloaded streaming-iterator v0.1.9
Downloaded sqlite v0.37.0
Downloaded spki v0.7.3
Downloaded serde_derive v1.0.228
Downloaded num-bigint-dig v0.8.6
Downloaded icu_normalizer_data v2.1.1
Downloaded icu_normalizer v2.1.1
Downloaded icu_locale_core v2.1.1
Downloaded wait-timeout v0.2.1
Downloaded libgit2-sys v0.18.3+1.9.2
Downloaded uuid-simd v0.8.0
Downloaded utf8parse v0.2.2
Downloaded utf8_iter v1.0.4
Downloaded tree-sitter-toml-ng v0.6.0
Downloaded tree-sitter-html v0.23.2
Downloaded value-bag-serde1 v1.12.0
Downloaded unit-prefix v0.5.2
Downloaded unicode-display-width v0.3.0
Downloaded toml_datetime v0.7.5+spec-1.1.0
Downloaded thiserror v2.0.18
Downloaded sqlite3-sys v0.18.0
Downloaded smallvec v1.15.1
Downloaded serde_core v1.0.228
Downloaded sem_safe v0.2.1
Downloaded synstructure v0.13.2
Downloaded cyphernet v0.5.2
Downloaded tree-sitter-json v0.24.8
Downloaded structured-logger v1.0.5
Downloaded gix-object v0.55.0
Downloaded getrandom v0.4.2
Downloaded quote v1.0.45
Downloaded sval_fmt v2.17.0
Downloaded strsim v0.11.1
Downloaded sval_ref v2.17.0
Downloaded sval_dynamic v2.17.0
Downloaded memchr v2.8.0
Downloaded referencing v0.30.0
Downloaded icu_properties v2.1.2
Downloaded pretty_assertions v1.4.1
Downloaded subtle v2.6.1
Downloaded ssh-cipher v0.2.0
Downloaded simd-adler32 v0.3.8
Downloaded proc-macro-error2 v2.0.1
Downloaded aho-corasick v1.1.4
Downloaded siphasher v0.3.11
Downloaded signature v1.6.4
Downloaded salsa20 v0.10.2
Downloaded semver v1.0.27
Downloaded fraction v0.15.3
Downloaded crossterm v0.29.0
Downloaded rustversion v1.0.22
Downloaded socks5-client v0.4.1
Downloaded siphasher v1.0.2
Downloaded shell-words v1.1.1
Downloaded sec1 v0.7.3
Downloaded same-file v1.0.6
Downloaded rustc-demangle v0.1.27
Downloaded icu_provider v2.1.1
Downloaded gix-ref v0.59.0
Downloaded rand_core v0.9.5
Downloaded radicle-git-ext v0.12.0
Downloaded proc-macro2 v1.0.106
Downloaded pkcs1 v0.7.5
Downloaded num-traits v0.2.19
Downloaded iana-time-zone v0.1.65
Downloaded gix-features v0.46.1
Downloaded crossbeam-channel v0.5.15
Downloaded backtrace v0.3.76
Downloaded scrypt v0.11.0
Downloaded linux-raw-sys v0.12.1
Downloaded sqlite3-src v0.7.0
Downloaded qcheck v1.0.0
Downloaded primeorder v0.13.6
Downloaded pin-project-lite v0.2.17
Downloaded parking_lot_core v0.9.12
Downloaded once_cell v1.21.4
Downloaded gix-url v0.35.2
Downloaded emojis v0.6.4
Downloaded gix-refspec v0.37.0
Downloaded scopeguard v1.2.0
Downloaded phf v0.11.3
Downloaded num-integer v0.1.46
Downloaded num-complex v0.4.6
Downloaded normalize-line-endings v0.3.0
Downloaded human-panic v2.0.6
Downloaded hmac v0.12.1
Downloaded pkg-config v0.3.32
Downloaded num-rational v0.4.2
Downloaded gix-credentials v0.36.0
Downloaded escargot v0.5.15
Downloaded rand_core v0.6.4
Downloaded rand_chacha v0.9.0
Downloaded polyval v0.6.2
Downloaded pkcs8 v0.10.2
Downloaded pem-rfc7468 v0.7.0
Downloaded gix-protocol v0.57.0
Downloaded litrs v1.0.0
Downloaded bytes v1.11.1
Downloaded radicle-std-ext v0.2.0
Downloaded litemap v0.8.1
Downloaded gix-revision v0.41.0
Downloaded clap v4.6.0
Downloaded amplify_derive v4.0.1
Downloaded amplify v4.9.0
Downloaded outref v0.5.2
Downloaded num-iter v0.1.45
Downloaded phf_shared v0.11.3
Downloaded pbkdf2 v0.12.2
Downloaded qcheck-macros v1.0.0
Downloaded icu_collections v2.1.1
Downloaded gix-error v0.1.0
Downloaded ref-cast v1.0.25
Downloaded proc-macro-error-attr2 v2.0.0
Downloaded gix-date v0.14.0
Downloaded gix-commitgraph v0.32.0
Downloaded ec25519 v0.1.0
Downloaded memmap2 v0.9.10
Downloaded is_terminal_polyfill v1.70.2
Downloaded idna_adapter v1.2.1
Downloaded gix-prompt v0.13.1
Downloaded blowfish v0.9.1
Downloaded anstream v1.0.0
Downloaded gix-commitgraph v0.33.0
Downloaded fnv v1.0.7
Downloaded der v0.7.10
Downloaded lexopt v0.3.2
Downloaded indicatif v0.18.4
Downloaded gix-revwalk v0.27.0
Downloaded gix-revwalk v0.26.0
Downloaded arc-swap v1.8.2
Downloaded gix-lock v21.0.1
Downloaded gix-config-value v0.17.1
Downloaded git-ref-format v0.6.0
Downloaded noise-framework v0.4.0
Downloaded matchers v0.2.0
Downloaded hash32 v0.3.1
Downloaded gix-validate v0.11.0
Downloaded gix-glob v0.24.0
Downloaded gix-chunk v0.5.0
Downloaded opaque-debug v0.3.1
Downloaded match-lookup v0.1.2
Downloaded jobserver v0.1.34
Downloaded gix-quote v0.6.2
Downloaded gix-error v0.0.0
Downloaded gix-command v0.7.1
Downloaded git-ref-format-macro v0.6.0
Downloaded amplify_num v0.5.3
Downloaded displaydoc v0.2.5
Downloaded anstyle-parse v0.2.7
Downloaded lazy_static v1.5.0
Downloaded gix-actor v0.39.0
Downloaded ed25519 v1.5.3
Downloaded diff v0.1.13
Downloaded cpufeatures v0.2.17
Downloaded generic-array v0.14.7
Downloaded fast-glob v0.3.3
Downloaded dyn-clone v1.0.20
Downloaded bytesize v2.3.1
Downloaded gix-odb v0.75.0
Downloaded data-encoding v2.10.0
Downloaded crossbeam-utils v0.8.21
Downloaded crc32fast v1.5.0
Downloaded base256emoji v1.0.2
Downloaded anstyle v1.0.14
Downloaded anstyle-parse v1.0.0
Downloaded gix-shallow v0.8.1
Downloaded gix-hashtable v0.12.0
Downloaded gix-chunk v0.6.0
Downloaded getrandom v0.3.4
Downloaded erased-serde v0.4.10
Downloaded dunce v1.0.5
Downloaded convert_case v0.10.0
Downloaded cfg-if v1.0.4
Downloaded cbc v0.1.2
Downloaded base16ct v0.2.0
Downloaded gix-object v0.56.0
Downloaded env_filter v1.0.0
Downloaded email_address v0.2.9
Downloaded data-encoding-macro v0.1.19
Downloaded cyphergraphy v0.3.0
Downloaded curve25519-dalek-derive v0.1.1
Downloaded ctr v0.9.2
Downloaded colorchoice v1.0.5
Downloaded base64ct v1.8.3
Downloaded base32 v0.4.0
Downloaded either v1.15.0
Downloaded ed25519 v2.2.3
Downloaded amplify_syn v2.0.1
Downloaded ghash v0.5.1
Downloaded byteorder v1.5.0
Downloaded gix-pack v0.65.0
Downloaded fluent-uri v0.3.2
Downloaded ecdsa v0.16.9
Downloaded group v0.13.0
Downloaded gix-path v0.11.1
Downloaded getrandom v0.2.17
Downloaded cc v1.2.57
Downloaded equivalent v1.0.2
Downloaded env_logger v0.11.9
Downloaded clap_lex v1.1.0
Downloaded bytecount v0.6.9
Downloaded digest v0.10.7
Downloaded derive_more v2.1.1
Downloaded cypheraddr v0.4.0
Downloaded clap_derive v4.6.0
Downloaded borrow-or-share v0.2.4
Downloaded anyhow v1.0.102
Downloaded ct-codecs v1.1.6
Downloaded colored v2.2.0
Downloaded cipher v0.4.4
Downloaded chacha20 v0.9.1
Downloaded block-buffer v0.10.4
Downloaded const-str v0.4.3
Downloaded block-padding v0.3.3
Downloaded const-oid v0.9.6
Downloaded clap_complete v4.6.0
Downloaded bitflags v2.11.0
Downloaded bit-vec v0.8.0
Downloaded base64 v0.22.1
Downloaded base64 v0.21.7
Downloaded anstyle-query v1.1.5
Downloaded autocfg v1.5.0
Downloaded gix-fs v0.19.1
Downloaded gix-date v0.13.0
Downloaded gix-actor v0.38.0
Downloaded ff v0.13.1
Downloaded faster-hex v0.10.0
Downloaded errno v0.3.14
Downloaded aes-gcm v0.10.3
Downloaded aes v0.8.4
Downloaded fastrand v2.3.0
Downloaded derive_more-impl v2.1.1
Downloaded crypto-bigint v0.5.5
Downloaded chacha20poly1305 v0.10.1
Downloaded adler2 v2.0.1
Downloaded addr2line v0.25.1
Downloaded aead v0.5.2
Downloaded anstream v0.6.21
Compiling libc v0.2.183
Compiling proc-macro2 v1.0.106
Compiling quote v1.0.45
Compiling unicode-ident v1.0.24
Checking cfg-if v1.0.4
Checking zeroize v1.8.2
Compiling version_check v0.9.5
Compiling typenum v1.19.0
Compiling generic-array v0.14.7
Checking memchr v2.8.0
Checking getrandom v0.2.17
Compiling syn v2.0.117
Checking rand_core v0.6.4
Compiling shlex v1.3.0
Checking crypto-common v0.1.7
Compiling find-msvc-tools v0.1.9
Checking subtle v2.6.1
Checking regex-syntax v0.8.10
Checking aho-corasick v1.1.4
Compiling jobserver v0.1.34
Compiling cc v1.2.57
Checking const-oid v0.9.6
Checking smallvec v1.15.1
Compiling serde_core v1.0.228
Checking regex-automata v0.4.14
Checking block-buffer v0.10.4
Checking digest v0.10.7
Checking cpufeatures v0.2.17
Compiling thiserror v2.0.18
Checking stable_deref_trait v1.2.1
Checking fastrand v2.3.0
Compiling parking_lot_core v0.9.12
Checking scopeguard v1.2.0
Checking lock_api v0.4.14
Checking tinyvec_macros v0.1.1
Checking parking_lot v0.12.5
Checking tinyvec v1.11.0
Compiling crc32fast v1.5.0
Checking bstr v1.12.1
Checking bitflags v2.11.0
Checking gix-trace v0.1.18
Checking unicode-normalization v0.1.25
Checking byteorder v1.5.0
Checking itoa v1.0.17
Checking gix-validate v0.11.0
Compiling typeid v1.0.3
Compiling erased-serde v0.4.10
Checking gix-utils v0.3.1
Compiling serde v1.0.228
Checking hashbrown v0.16.1
Compiling thiserror-impl v2.0.18
Checking same-file v1.0.6
Checking walkdir v2.5.0
Compiling serde_derive v1.0.228
Checking prodash v31.0.0
Checking zlib-rs v0.6.3
Checking serde_fmt v1.1.0
Compiling heapless v0.8.0
Checking value-bag-serde1 v1.12.0
Checking hash32 v0.3.1
Compiling synstructure v0.13.2
Checking gix-path v0.11.1
Checking gix-features v0.46.1
Checking value-bag v1.12.0
Checking faster-hex v0.10.0
Compiling zerofrom-derive v0.1.6
Compiling yoke-derive v0.8.1
Checking log v0.4.29
Checking sha1 v0.10.6
Checking sha1-checked v0.10.0
Compiling rustix v1.1.4
Compiling pkg-config v0.3.32
Checking zerofrom v0.1.6
Checking yoke v0.8.1
Checking gix-hash v0.22.1
Compiling zerovec-derive v0.11.2
Checking block-padding v0.3.3
Compiling autocfg v1.5.0
Compiling zerocopy v0.8.42
Compiling libm v0.2.16
Checking linux-raw-sys v0.12.1
Compiling num-traits v0.2.19
Checking inout v0.1.4
Compiling displaydoc v0.2.5
Checking sha2 v0.10.9
Checking zerovec v0.11.5
Compiling getrandom v0.4.2
Checking cipher v0.4.4
Checking tinystr v0.8.2
Checking der v0.7.10
Checking percent-encoding v2.3.2
Checking writeable v0.6.2
Checking litemap v0.8.1
Checking once_cell v1.21.4
Checking icu_locale_core v2.1.1
Checking potential_utf v0.1.4
Checking zerotrie v0.2.3
Compiling icu_properties_data v2.1.2
Compiling syn v1.0.109
Compiling zmij v1.0.21
Compiling icu_normalizer_data v2.1.1
Compiling thiserror v1.0.69
Checking icu_provider v2.1.1
Checking icu_collections v2.1.1
Compiling thiserror-impl v1.0.69
Checking equivalent v1.0.2
Compiling serde_json v1.0.149
Checking indexmap v2.13.0
Checking ppv-lite86 v0.2.21
Checking num-integer v0.1.46
Checking hmac v0.12.1
Checking universal-hash v0.5.1
Checking opaque-debug v0.3.1
Compiling ref-cast v1.0.25
Compiling vcpkg v0.2.15
Compiling tree-sitter-language v0.1.7
Checking icu_normalizer v2.1.1
Checking icu_properties v2.1.2
Compiling libz-sys v1.1.25
Checking tempfile v3.27.0
Checking spki v0.7.3
Compiling ref-cast-impl v1.0.25
Checking signature v2.2.0
Checking ff v0.13.1
Checking base16ct v0.2.0
Checking spin v0.9.8
Checking sec1 v0.7.3
Checking lazy_static v1.5.0
Checking group v0.13.0
Checking idna_adapter v1.2.1
Checking rand_chacha v0.3.1
Checking crypto-bigint v0.5.5
Checking dyn-clone v1.0.20
Checking utf8_iter v1.0.4
Checking rand v0.8.5
Checking idna v1.1.0
Compiling amplify_syn v2.0.1
Checking num-iter v0.1.45
Compiling libgit2-sys v0.18.3+1.9.2
Checking aead v0.5.2
Compiling semver v1.0.27
Checking elliptic-curve v0.13.8
Checking signature v1.6.4
Compiling amplify_derive v4.0.1
Checking ed25519 v1.5.3
Compiling rustc_version v0.4.1
Checking poly1305 v0.8.0
Checking rfc6979 v0.4.0
Checking form_urlencoded v1.2.2
Checking chacha20 v0.9.1
Compiling serde_derive_internals v0.29.1
Checking ascii v1.1.0
Checking amplify_num v0.5.3
Checking ct-codecs v1.1.6
Checking ec25519 v0.1.0
Compiling schemars_derive v1.2.1
Checking url v2.5.8
Checking amplify v4.9.0
Checking ecdsa v0.16.9
Compiling curve25519-dalek v4.1.3
Checking primeorder v0.13.6
Checking git-ref-format-core v0.6.0
Checking polyval v0.6.2
Checking base64ct v1.8.3
Compiling num-bigint-dig v0.8.6
Checking ghash v0.5.1
Checking pem-rfc7468 v0.7.0
Checking schemars v1.2.1
Checking cyphergraphy v0.3.0
Checking pkcs8 v0.10.2
Checking pbkdf2 v0.12.2
Checking aes v0.8.4
Checking ctr v0.9.2
Compiling sqlite3-src v0.7.0
Compiling curve25519-dalek-derive v0.1.1
Checking keccak v0.1.6
Checking aes-gcm v0.10.3
Checking sha3 v0.10.8
Checking pkcs1 v0.7.5
Checking ssh-encoding v0.2.0
Checking ed25519 v2.2.3
Checking blowfish v0.9.1
Checking cbc v0.1.2
Compiling data-encoding v2.10.0
Checking base32 v0.4.0
Checking cypheraddr v0.4.0
Compiling data-encoding-macro-internal v0.1.17
Checking rsa v0.9.10
Checking ssh-cipher v0.2.0
Checking bcrypt-pbkdf v0.10.0
Checking ed25519-dalek v2.2.0
Checking p256 v0.13.2
Checking p384 v0.13.1
Checking p521 v0.13.3
Checking chacha20poly1305 v0.10.1
Checking qcheck v1.0.0
Compiling match-lookup v0.1.2
Checking jiff v0.2.23
Checking const-str v0.4.3
Checking data-encoding-macro v0.1.19
Checking base256emoji v1.0.2
Checking ssh-key v0.6.7
Checking noise-framework v0.4.0
Checking socks5-client v0.4.1
Checking secrecy v0.10.3
Compiling crossbeam-utils v0.8.21
Checking base-x v0.2.11
Checking multibase v0.9.2
Checking ssh-agent-lib v0.5.2
Checking cyphernet v0.5.2
Checking winnow v0.7.15
Checking crossbeam-channel v0.5.15
Checking anstyle-query v1.1.5
Checking utf8parse v0.2.2
Checking nonempty v0.9.0
Checking gix-hashtable v0.12.0
Checking siphasher v1.0.2
Checking radicle-git-metadata v0.2.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-git-metadata)
Checking gix-error v0.1.0
Checking radicle-dag v0.10.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-dag)
Checking memmap2 v0.9.10
Checking colorchoice v1.0.5
Checking is_terminal_polyfill v1.70.2
Checking anstyle v1.0.14
Checking radicle-git-ref-format v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-git-ref-format)
Checking iana-time-zone v0.1.65
Compiling radicle v0.23.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle)
Checking base64 v0.21.7
Checking chrono v0.4.44
Checking radicle-localtime v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-localtime)
Checking colored v2.2.0
Checking serde-untagged v0.1.9
Checking gix-error v0.0.0
Checking bytesize v2.3.1
Checking errno v0.3.14
Checking dunce v1.0.5
Checking fast-glob v0.3.3
Checking gix-date v0.14.0
Checking anstyle-parse v0.2.7
Checking gix-actor v0.39.0
Checking anstream v0.6.21
Checking gix-date v0.13.0
Checking gix-fs v0.19.1
Checking gix-actor v0.38.0
Checking gix-tempfile v21.0.1
Checking gix-object v0.56.0
Checking gix-chunk v0.6.0
Checking gix-quote v0.6.2
Checking gix-commitgraph v0.33.0
Checking gix-object v0.55.0
Checking gix-chunk v0.5.0
Checking mio v1.1.1
Checking sem_safe v0.2.1
Checking either v1.15.0
Checking shell-words v1.1.1
Checking signals_receipts v0.2.5
Checking gix-command v0.7.1
Checking gix-commitgraph v0.32.0
Checking gix-revwalk v0.27.0
Compiling rustversion v1.0.22
Compiling object v0.37.3
Checking gix-revwalk v0.26.0
Checking gix-lock v21.0.1
Checking gix-url v0.35.2
Checking gix-config-value v0.17.1
Checking gix-sec v0.13.1
Checking gimli v0.32.3
Compiling signal-hook v0.3.18
Checking adler2 v2.0.1
Compiling unicode-segmentation v1.12.0
Compiling convert_case v0.10.0
Checking miniz_oxide v0.8.9
Checking gix-prompt v0.13.1
Checking gix-traverse v0.52.0
Checking addr2line v0.25.1
Checking gix-revision v0.41.0
Checking radicle-signals v0.11.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-signals)
Checking gix-diff v0.58.0
Checking signal-hook-registry v1.4.8
Checking gix-packetline v0.21.1
Checking gix-glob v0.24.0
Compiling tree-sitter v0.24.7
Compiling anyhow v1.0.102
Checking rustc-demangle v0.1.27
Checking backtrace v0.3.76
Checking gix-refspec v0.37.0
Checking gix-transport v0.54.0
Checking gix-pack v0.65.0
Checking arc-swap v1.8.2
Checking gix-credentials v0.36.0
Compiling derive_more-impl v2.1.1
Checking gix-shallow v0.8.1
Checking gix-ref v0.59.0
Checking gix-negotiate v0.27.0
Compiling maybe-async v0.2.10
Checking regex v1.12.3
Compiling proc-macro-error-attr2 v2.0.0
Checking unicode-width v0.2.2
Compiling portable-atomic v1.13.1
Compiling simd-adler32 v0.3.8
Compiling getrandom v0.3.4
Compiling litrs v1.0.0
Compiling document-features v0.2.12
Compiling proc-macro-error2 v2.0.1
Checking gix-protocol v0.57.0
Checking derive_more v2.1.1
Checking gix-odb v0.75.0
Checking signal-hook-mio v0.2.5
Compiling xattr v1.6.1
Compiling filetime v0.2.27
Checking anstyle-parse v1.0.0
Checking uuid v1.22.0
Checking bytes v1.11.1
Checking anstream v1.0.0
Compiling tar v0.4.45
Compiling flate2 v1.1.9
Checking crossterm v0.29.0
Compiling git-ref-format-macro v0.6.0
Checking console v0.16.3
Checking snapbox-macros v0.3.10
Checking salsa20 v0.10.2
Checking strsim v0.11.1
Checking clap_lex v1.1.0
Checking streaming-iterator v0.1.9
Checking siphasher v0.3.11
Checking unit-prefix v0.5.2
Compiling heck v0.5.0
Checking normalize-line-endings v0.3.0
Checking similar v2.7.0
Compiling clap_derive v4.6.0
Checking snapbox v0.4.17
Checking indicatif v0.18.4
Checking bloomy v1.2.0
Checking sqlite3-sys v0.18.0
Checking clap_builder v4.6.0
Checking sqlite v0.37.0
Compiling radicle-surf v0.27.1
Checking radicle-crypto v0.16.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-crypto)
Checking scrypt v0.11.0
Checking git-ref-format v0.6.0
Checking inquire v0.9.4
Checking unicode-display-width v0.3.0
Checking systemd-journal-logger v2.2.2
Checking serde_spanned v1.0.4
Checking toml_datetime v0.7.5+spec-1.1.0
Compiling tree-sitter-rust v0.23.3
Compiling tree-sitter-html v0.23.2
Compiling tree-sitter-json v0.24.8
Compiling tree-sitter-bash v0.23.3
Compiling tree-sitter-css v0.23.2
Compiling tree-sitter-typescript v0.23.2
Compiling tree-sitter-python v0.23.6
Compiling tree-sitter-ruby v0.23.1
Compiling tree-sitter-toml-ng v0.6.0
Compiling tree-sitter-c v0.23.4
Compiling tree-sitter-go v0.23.4
Compiling tree-sitter-md v0.3.2
Checking pin-project-lite v0.2.17
Checking radicle-std-ext v0.2.0
Checking toml_writer v1.0.7+spec-1.1.0
Checking tokio v1.50.0
Checking toml v0.9.12+spec-1.1.0
Checking clap v4.6.0
Checking sysinfo v0.37.2
Compiling radicle-cli v0.20.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-cli)
Checking yansi v1.0.1
Compiling radicle-node v0.19.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-node)
Checking diff v0.1.13
Checking pretty_assertions v1.4.1
Checking human-panic v2.0.6
Checking clap_complete v4.6.0
Checking structured-logger v1.0.5
Checking radicle-systemd v0.12.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-systemd)
Checking tree-sitter-highlight v0.24.7
Checking itertools v0.14.0
Compiling qcheck-macros v1.0.0
Checking socket2 v0.5.10
Checking lexopt v0.3.2
Checking timeago v0.4.2
Checking humantime v2.3.0
Compiling escargot v0.5.15
Checking bit-vec v0.8.0
Checking bit-set v0.8.0
Checking rand_core v0.9.5
Checking num-bigint v0.4.6
Compiling ahash v0.8.12
Checking num-complex v0.4.6
Checking env_filter v1.0.0
Checking borrow-or-share v0.2.4
Checking env_logger v0.11.9
Checking fluent-uri v0.3.2
Checking phf_shared v0.11.3
Compiling test-log-macros v0.2.19
Checking wait-timeout v0.2.1
Checking num-rational v0.4.2
Checking vsimd v0.8.0
Checking quick-error v1.2.3
Checking num v0.4.3
Compiling radicle-remote-helper v0.16.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-remote-helper)
Checking outref v0.5.2
Checking fnv v1.0.7
Checking rusty-fork v0.3.1
Checking uuid-simd v0.8.0
Checking fraction v0.15.3
Checking test-log v0.2.19
Checking referencing v0.30.0
Checking phf v0.11.3
Checking rand v0.9.2
Checking rand_chacha v0.9.0
Checking rand_xorshift v0.4.0
Checking fancy-regex v0.14.0
Checking email_address v0.2.9
Checking unarray v0.1.4
Checking base64 v0.22.1
Checking bytecount v0.6.9
Checking num-cmp v0.1.0
Checking proptest v1.10.0
Checking emojis v0.6.4
Checking jsonschema v0.30.0
Compiling pastey v0.2.1
Checking radicle-windows v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-windows)
Checking git2 v0.20.4
Checking radicle-oid v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-oid)
Checking radicle-git-ext v0.12.0
Checking radicle-term v0.17.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-term)
Checking radicle-cob v0.19.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-cob)
Checking radicle-core v0.2.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-core)
Checking radicle-fetch v0.19.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-fetch)
Checking radicle-cli-test v0.13.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-cli-test)
Checking radicle-protocol v0.7.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-protocol)
Checking radicle-schemars v0.7.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-schemars)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 43.11s
+ cargo build --all-targets --workspace
Compiling libc v0.2.183
Compiling cfg-if v1.0.4
Compiling zeroize v1.8.2
Compiling typenum v1.19.0
Compiling memchr v2.8.0
Compiling shlex v1.3.0
Compiling subtle v2.6.1
Compiling regex-syntax v0.8.10
Compiling generic-array v0.14.7
Compiling aho-corasick v1.1.4
Compiling getrandom v0.2.17
Compiling jobserver v0.1.34
Compiling rand_core v0.6.4
Compiling crypto-common v0.1.7
Compiling cc v1.2.57
Compiling const-oid v0.9.6
Compiling smallvec v1.15.1
Compiling serde_core v1.0.228
Compiling regex-automata v0.4.14
Compiling block-buffer v0.10.4
Compiling digest v0.10.7
Compiling cpufeatures v0.2.17
Compiling stable_deref_trait v1.2.1
Compiling thiserror v2.0.18
Compiling fastrand v2.3.0
Compiling scopeguard v1.2.0
Compiling lock_api v0.4.14
Compiling parking_lot_core v0.9.12
Compiling bitflags v2.11.0
Compiling parking_lot v0.12.5
Compiling tinyvec_macros v0.1.1
Compiling tinyvec v1.11.0
Compiling gix-trace v0.1.18
Compiling byteorder v1.5.0
Compiling crc32fast v1.5.0
Compiling itoa v1.0.17
Compiling unicode-normalization v0.1.25
Compiling typeid v1.0.3
Compiling hashbrown v0.16.1
Compiling same-file v1.0.6
Compiling walkdir v2.5.0
Compiling gix-utils v0.3.1
Compiling erased-serde v0.4.10
Compiling prodash v31.0.0
Compiling zlib-rs v0.6.3
Compiling serde v1.0.228
Compiling serde_fmt v1.1.0
Compiling bstr v1.12.1
Compiling value-bag-serde1 v1.12.0
Compiling hash32 v0.3.1
Compiling heapless v0.8.0
Compiling gix-validate v0.11.0
Compiling value-bag v1.12.0
Compiling gix-path v0.11.1
Compiling log v0.4.29
Compiling faster-hex v0.10.0
Compiling zerofrom v0.1.6
Compiling sha1 v0.10.6
Compiling yoke v0.8.1
Compiling sha1-checked v0.10.0
Compiling block-padding v0.3.3
Compiling linux-raw-sys v0.12.1
Compiling zerovec v0.11.5
Compiling inout v0.1.4
Compiling sha2 v0.10.9
Compiling gix-features v0.46.1
Compiling gix-hash v0.22.1
Compiling rustix v1.1.4
Compiling cipher v0.4.4
Compiling libm v0.2.16
Compiling zerocopy v0.8.42
Compiling getrandom v0.4.2
Compiling tinystr v0.8.2
Compiling der v0.7.10
Compiling num-traits v0.2.19
Compiling once_cell v1.21.4
Compiling percent-encoding v2.3.2
Compiling litemap v0.8.1
Compiling writeable v0.6.2
Compiling icu_locale_core v2.1.1
Compiling zerotrie v0.2.3
Compiling potential_utf v0.1.4
Compiling icu_collections v2.1.1
Compiling icu_provider v2.1.1
Compiling equivalent v1.0.2
Compiling indexmap v2.13.0
Compiling zmij v1.0.21
Compiling icu_normalizer_data v2.1.1
Compiling icu_properties_data v2.1.2
Compiling num-integer v0.1.46
Compiling hmac v0.12.1
Compiling universal-hash v0.5.1
Compiling opaque-debug v0.3.1
Compiling ppv-lite86 v0.2.21
Compiling libz-sys v1.1.25
Compiling serde_json v1.0.149
Compiling thiserror v1.0.69
Compiling icu_properties v2.1.2
Compiling icu_normalizer v2.1.1
Compiling tempfile v3.27.0
Compiling spki v0.7.3
Compiling signature v2.2.0
Compiling ff v0.13.1
Compiling base16ct v0.2.0
Compiling spin v0.9.8
Compiling sec1 v0.7.3
Compiling lazy_static v1.5.0
Compiling idna_adapter v1.2.1
Compiling group v0.13.0
Compiling ref-cast v1.0.25
Compiling rand_chacha v0.3.1
Compiling crypto-bigint v0.5.5
Compiling utf8_iter v1.0.4
Compiling dyn-clone v1.0.20
Compiling idna v1.1.0
Compiling rand v0.8.5
Compiling num-iter v0.1.45
Compiling libgit2-sys v0.18.3+1.9.2
Compiling aead v0.5.2
Compiling signature v1.6.4
Compiling ed25519 v1.5.3
Compiling poly1305 v0.8.0
Compiling rfc6979 v0.4.0
Compiling form_urlencoded v1.2.2
Compiling chacha20 v0.9.1
Compiling elliptic-curve v0.13.8
Compiling ascii v1.1.0
Compiling amplify_num v0.5.3
Compiling ct-codecs v1.1.6
Compiling ec25519 v0.1.0
Compiling primeorder v0.13.6
Compiling ecdsa v0.16.9
Compiling url v2.5.8
Compiling git-ref-format-core v0.6.0
Compiling polyval v0.6.2
Compiling amplify v4.9.0
Compiling base64ct v1.8.3
Compiling pem-rfc7468 v0.7.0
Compiling cyphergraphy v0.3.0
Compiling ghash v0.5.1
Compiling schemars v1.2.1
Compiling pkcs8 v0.10.2
Compiling pbkdf2 v0.12.2
Compiling aes v0.8.4
Compiling ctr v0.9.2
Compiling sqlite3-src v0.7.0
Compiling keccak v0.1.6
Compiling sha3 v0.10.8
Compiling aes-gcm v0.10.3
Compiling curve25519-dalek v4.1.3
Compiling pkcs1 v0.7.5
Compiling ssh-encoding v0.2.0
Compiling num-bigint-dig v0.8.6
Compiling ed25519 v2.2.3
Compiling blowfish v0.9.1
Compiling cbc v0.1.2
Compiling base32 v0.4.0
Compiling cypheraddr v0.4.0
Compiling rsa v0.9.10
Compiling ssh-cipher v0.2.0
Compiling bcrypt-pbkdf v0.10.0
Compiling ed25519-dalek v2.2.0
Compiling p256 v0.13.2
Compiling p384 v0.13.1
Compiling p521 v0.13.3
Compiling chacha20poly1305 v0.10.1
Compiling qcheck v1.0.0
Compiling data-encoding v2.10.0
Compiling const-str v0.4.3
Compiling jiff v0.2.23
Compiling base256emoji v1.0.2
Compiling data-encoding-macro v0.1.19
Compiling ssh-key v0.6.7
Compiling noise-framework v0.4.0
Compiling socks5-client v0.4.1
Compiling secrecy v0.10.3
Compiling base-x v0.2.11
Compiling multibase v0.9.2
Compiling ssh-agent-lib v0.5.2
Compiling cyphernet v0.5.2
Compiling crossbeam-utils v0.8.21
Compiling winnow v0.7.15
Compiling crossbeam-channel v0.5.15
Compiling utf8parse v0.2.2
Compiling anstyle-query v1.1.5
Compiling gix-hashtable v0.12.0
Compiling nonempty v0.9.0
Compiling siphasher v1.0.2
Compiling gix-error v0.1.0
Compiling radicle-dag v0.10.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-dag)
Compiling radicle-git-metadata v0.2.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-git-metadata)
Compiling memmap2 v0.9.10
Compiling is_terminal_polyfill v1.70.2
Compiling colorchoice v1.0.5
Compiling anstyle v1.0.14
Compiling radicle-git-ref-format v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-git-ref-format)
Compiling iana-time-zone v0.1.65
Compiling radicle v0.23.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle)
Compiling base64 v0.21.7
Compiling chrono v0.4.44
Compiling radicle-localtime v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-localtime)
Compiling colored v2.2.0
Compiling tree-sitter-language v0.1.7
Compiling gix-error v0.0.0
Compiling serde-untagged v0.1.9
Compiling bytesize v2.3.1
Compiling errno v0.3.14
Compiling fast-glob v0.3.3
Compiling dunce v1.0.5
Compiling gix-date v0.14.0
Compiling anstyle-parse v0.2.7
Compiling adler2 v2.0.1
Compiling gix-actor v0.39.0
Compiling anstream v0.6.21
Compiling gix-date v0.13.0
Compiling gix-fs v0.19.1
Compiling gix-tempfile v21.0.1
Compiling gix-actor v0.38.0
Compiling gix-object v0.56.0
Compiling gix-chunk v0.6.0
Compiling gix-quote v0.6.2
Compiling gix-commitgraph v0.33.0
Compiling gix-object v0.55.0
Compiling gix-chunk v0.5.0
Compiling mio v1.1.1
Compiling sem_safe v0.2.1
Compiling either v1.15.0
Compiling shell-words v1.1.1
Compiling gix-command v0.7.1
Compiling signals_receipts v0.2.5
Compiling gix-commitgraph v0.32.0
Compiling gix-revwalk v0.27.0
Compiling unicode-segmentation v1.12.0
Compiling gix-revwalk v0.26.0
Compiling gix-lock v21.0.1
Compiling gix-url v0.35.2
Compiling gix-config-value v0.17.1
Compiling gix-sec v0.13.1
Compiling gimli v0.32.3
Compiling gix-prompt v0.13.1
Compiling convert_case v0.10.0
Compiling gix-traverse v0.52.0
Compiling object v0.37.3
Compiling addr2line v0.25.1
Compiling gix-revision v0.41.0
Compiling radicle-signals v0.11.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-signals)
Compiling gix-diff v0.58.0
Compiling miniz_oxide v0.8.9
Compiling signal-hook-registry v1.4.8
Compiling gix-glob v0.24.0
Compiling gix-packetline v0.21.1
Compiling tree-sitter v0.24.7
Compiling rustc-demangle v0.1.27
Compiling backtrace v0.3.76
Compiling sqlite3-sys v0.18.0
Compiling sqlite v0.37.0
Compiling gix-transport v0.54.0
Compiling radicle-crypto v0.16.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-crypto)
Compiling gix-refspec v0.37.0
Compiling signal-hook v0.3.18
Compiling gix-pack v0.65.0
Compiling arc-swap v1.8.2
Compiling derive_more-impl v2.1.1
Compiling gix-credentials v0.36.0
Compiling gix-shallow v0.8.1
Compiling gix-ref v0.59.0
Compiling gix-negotiate v0.27.0
Compiling regex v1.12.3
Compiling unicode-width v0.2.2
Compiling gix-protocol v0.57.0
Compiling gix-odb v0.75.0
Compiling derive_more v2.1.1
Compiling signal-hook-mio v0.2.5
Compiling xattr v1.6.1
Compiling anstyle-parse v1.0.0
Compiling uuid v1.22.0
Compiling filetime v0.2.27
Compiling bytes v1.11.1
Compiling tar v0.4.45
Compiling git-ref-format-macro v0.6.0
Compiling anstream v1.0.0
Compiling crossterm v0.29.0
Compiling flate2 v1.1.9
Compiling portable-atomic v1.13.1
Compiling getrandom v0.3.4
Compiling anyhow v1.0.102
Compiling console v0.16.3
Compiling snapbox-macros v0.3.10
Compiling salsa20 v0.10.2
Compiling similar v2.7.0
Compiling clap_lex v1.1.0
Compiling strsim v0.11.1
Compiling unit-prefix v0.5.2
Compiling streaming-iterator v0.1.9
Compiling normalize-line-endings v0.3.0
Compiling siphasher v0.3.11
Compiling bloomy v1.2.0
Compiling snapbox v0.4.17
Compiling indicatif v0.18.4
Compiling clap_builder v4.6.0
Compiling scrypt v0.11.0
Compiling radicle-surf v0.27.1
Compiling inquire v0.9.4
Compiling git-ref-format v0.6.0
Compiling unicode-display-width v0.3.0
Compiling systemd-journal-logger v2.2.2
Compiling toml_datetime v0.7.5+spec-1.1.0
Compiling serde_spanned v1.0.4
Compiling tree-sitter-bash v0.23.3
Compiling tree-sitter-html v0.23.2
Compiling tree-sitter-md v0.3.2
Compiling tree-sitter-go v0.23.4
Compiling tree-sitter-c v0.23.4
Compiling tree-sitter-python v0.23.6
Compiling tree-sitter-ruby v0.23.1
Compiling tree-sitter-css v0.23.2
Compiling tree-sitter-rust v0.23.3
Compiling tree-sitter-toml-ng v0.6.0
Compiling tree-sitter-typescript v0.23.2
Compiling tree-sitter-json v0.24.8
Compiling toml_writer v1.0.7+spec-1.1.0
Compiling pin-project-lite v0.2.17
Compiling radicle-std-ext v0.2.0
Compiling tokio v1.50.0
Compiling toml v0.9.12+spec-1.1.0
Compiling clap v4.6.0
Compiling sysinfo v0.37.2
Compiling radicle-cli v0.20.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-cli)
Compiling diff v0.1.13
Compiling yansi v1.0.1
Compiling radicle-node v0.19.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-node)
Compiling pretty_assertions v1.4.1
Compiling human-panic v2.0.6
Compiling clap_complete v4.6.0
Compiling structured-logger v1.0.5
Compiling radicle-systemd v0.12.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-systemd)
Compiling tree-sitter-highlight v0.24.7
Compiling itertools v0.14.0
Compiling socket2 v0.5.10
Compiling lexopt v0.3.2
Compiling humantime v2.3.0
Compiling timeago v0.4.2
Compiling bit-vec v0.8.0
Compiling bit-set v0.8.0
Compiling escargot v0.5.15
Compiling rand_core v0.9.5
Compiling num-bigint v0.4.6
Compiling num-complex v0.4.6
Compiling env_filter v1.0.0
Compiling borrow-or-share v0.2.4
Compiling fluent-uri v0.3.2
Compiling num-rational v0.4.2
Compiling num v0.4.3
Compiling env_logger v0.11.9
Compiling ahash v0.8.12
Compiling git2 v0.20.4
Compiling phf_shared v0.11.3
Compiling wait-timeout v0.2.1
Compiling radicle-remote-helper v0.16.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-remote-helper)
Compiling outref v0.5.2
Compiling vsimd v0.8.0
Compiling fnv v1.0.7
Compiling quick-error v1.2.3
Compiling rusty-fork v0.3.1
Compiling test-log v0.2.19
Compiling phf v0.11.3
Compiling uuid-simd v0.8.0
Compiling referencing v0.30.0
Compiling fraction v0.15.3
Compiling rand v0.9.2
Compiling rand_xorshift v0.4.0
Compiling rand_chacha v0.9.0
Compiling fancy-regex v0.14.0
Compiling email_address v0.2.9
Compiling bytecount v0.6.9
Compiling base64 v0.22.1
Compiling num-cmp v0.1.0
Compiling unarray v0.1.4
Compiling proptest v1.10.0
Compiling jsonschema v0.30.0
Compiling radicle-oid v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-oid)
Compiling radicle-cob v0.19.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-cob)
Compiling radicle-core v0.2.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-core)
Compiling radicle-git-ext v0.12.0
Compiling radicle-term v0.17.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-term)
Compiling emojis v0.6.4
Compiling radicle-windows v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-windows)
Compiling radicle-fetch v0.19.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-fetch)
Compiling radicle-protocol v0.7.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-protocol)
Compiling radicle-cli-test v0.13.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-cli-test)
Compiling radicle-schemars v0.7.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-schemars)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 59.46s
+ cargo doc --workspace --no-deps --all-features
Checking regex-automata v0.4.14
Compiling num-traits v0.2.19
Compiling syn v1.0.109
Checking once_cell v1.21.4
Checking tempfile v3.27.0
Checking idna v1.1.0
Checking url v2.5.8
Checking num-integer v0.1.46
Checking git2 v0.20.4
Checking num-iter v0.1.45
Checking num-bigint-dig v0.8.6
Compiling amplify_syn v2.0.1
Checking bstr v1.12.1
Checking gix-validate v0.11.0
Checking git-ref-format-core v0.6.0
Checking gix-path v0.11.1
Checking gix-features v0.46.1
Compiling amplify_derive v4.0.1
Checking rsa v0.9.10
Checking gix-hash v0.22.1
Checking radicle-git-ref-format v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-git-ref-format)
Checking ssh-key v0.6.7
Checking gix-hashtable v0.12.0
Checking radicle-oid v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-oid)
Checking gix-error v0.1.0
Checking rusty-fork v0.3.1
Checking gix-error v0.0.0
Checking proptest v1.10.0
Checking radicle-git-metadata v0.2.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-git-metadata)
Checking radicle-dag v0.10.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-dag)
Checking gix-date v0.14.0
Checking ssh-agent-lib v0.5.2
Checking chrono v0.4.44
Checking radicle-localtime v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-localtime)
Checking gix-actor v0.39.0
Checking amplify v4.9.0
Checking gix-date v0.13.0
Checking gix-fs v0.19.1
Checking cyphergraphy v0.3.0
Checking gix-tempfile v21.0.1
Checking cypheraddr v0.4.0
Checking noise-framework v0.4.0
Checking gix-actor v0.38.0
Checking gix-object v0.56.0
Checking gix-chunk v0.6.0
Checking gix-quote v0.6.2
Checking socks5-client v0.4.1
Checking gix-commitgraph v0.33.0
Checking cyphernet v0.5.2
Checking radicle-crypto v0.16.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-crypto)
Checking gix-object v0.55.0
Checking gix-chunk v0.5.0
Checking gix-command v0.7.1
Checking gix-commitgraph v0.32.0
Checking radicle-core v0.2.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-core)
Checking radicle-cob v0.19.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-cob)
Checking gix-revwalk v0.27.0
Checking gix-lock v21.0.1
Checking gix-config-value v0.17.1
Checking gix-url v0.35.2
Checking gix-revwalk v0.26.0
Checking gix-diff v0.58.0
Checking gix-prompt v0.13.1
Checking radicle v0.23.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle)
Checking gix-traverse v0.52.0
Checking gix-revision v0.41.0
Checking radicle-signals v0.11.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-signals)
Checking gix-glob v0.24.0
Checking gix-packetline v0.21.1
Checking regex v1.12.3
Checking gix-refspec v0.37.0
Checking gix-transport v0.54.0
Checking tree-sitter v0.24.7
Checking gix-pack v0.65.0
Checking git-ref-format v0.6.0
Checking inquire v0.9.4
Checking gix-credentials v0.36.0
Checking gix-ref v0.59.0
Checking gix-shallow v0.8.1
Checking gix-negotiate v0.27.0
Checking radicle-git-ext v0.12.0
Checking gix-protocol v0.57.0
Checking gix-odb v0.75.0
Checking uuid v1.22.0
Compiling radicle-cli v0.20.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-cli)
Checking human-panic v2.0.6
Checking radicle-surf v0.27.1
Checking tree-sitter-toml-ng v0.6.0
Checking radicle-term v0.17.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-term)
Checking tree-sitter-highlight v0.24.7
Checking radicle-systemd v0.12.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-systemd)
Documenting radicle-systemd v0.12.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-systemd)
Documenting radicle-term v0.17.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-term)
Documenting radicle v0.23.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle)
Documenting radicle-cob v0.19.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-cob)
Documenting radicle-core v0.2.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-core)
Documenting radicle-signals v0.11.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-signals)
Documenting radicle-crypto v0.16.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-crypto)
Documenting radicle-oid v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-oid)
Documenting radicle-git-ref-format v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-git-ref-format)
Documenting radicle-localtime v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-localtime)
Documenting radicle-git-metadata v0.2.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-git-metadata)
Documenting radicle-dag v0.10.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-dag)
Documenting radicle-windows v0.1.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-windows)
Checking radicle-fetch v0.19.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-fetch)
Documenting radicle-cli v0.20.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-cli)
Documenting radicle-schemars v0.7.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-schemars)
Checking radicle-protocol v0.7.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-protocol)
Documenting radicle-protocol v0.7.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-protocol)
Documenting radicle-node v0.19.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-node)
Documenting radicle-fetch v0.19.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-fetch)
Documenting radicle-cli-test v0.13.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-cli-test)
Documenting radicle-remote-helper v0.16.0 (/7cd05d0d-27df-4be6-a215-8460a6240a7c/w/crates/radicle-remote-helper)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 10.39s
Generated /7cd05d0d-27df-4be6-a215-8460a6240a7c/w/target/doc/radicle/index.html and 20 other files
+ cargo test --workspace --no-fail-fast
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.14s
Running unittests src/lib.rs (target/debug/deps/radicle-65512d8015bfc8b3)
running 345 tests
test canonical::formatter::test::ascii_control_characters ... ok
test canonical::formatter::test::ordered_nested_object ... ok
test canonical::formatter::test::securesystemslib_asserts ... ok
test cob::cache::migrations::_2::tests::test_patch_json_deserialization ... ok
test cob::cache::tests::test_migrate_to ... ok
test cob::cache::migrations::_2::tests::test_migration_2 ... ok
test cob::cache::tests::test_check_version ... ok
test cob::common::test::test_color ... ok
test cob::common::test::test_title ... ok
test cob::common::test::test_emojis ... ok
test cob::identity::test::prop_json_eq_str ... ok
test cob::identity::test::test_identity_redact_revision ... ok
test cob::identity::test::test_identity_remove_delegate_concurrent ... ok
test cob::identity::test::test_identity_reject_concurrent ... ok
test cob::identity::test::test_identity_update_rejected ... ok
test cob::identity::test::test_identity_updates ... ok
test cob::issue::cache::tests::test_counts ... ok
test cob::issue::cache::tests::test_get ... ok
test cob::issue::cache::tests::test_is_empty ... ok
test cob::issue::cache::tests::test_list ... ok
test cob::issue::cache::tests::test_list_by_status ... ok
test cob::issue::cache::tests::test_remove ... ok
test cob::identity::test::test_valid_identity ... ok
test cob::identity::test::test_identity_updates_concurrent ... ok
test cob::issue::test::test_embeds_edit ... ok
test cob::issue::test::test_embeds ... ok
test cob::issue::test::test_invalid_actions ... ok
test cob::identity::test::test_identity_updates_concurrent_outdated ... ok
test cob::issue::test::test_invalid_cob ... ok
test cob::issue::test::test_invalid_tx ... ok
test cob::issue::test::test_concurrency ... ok
test cob::issue::test::test_invalid_tx_reference ... ok
test cob::issue::test::test_issue_all ... ok
test cob::issue::test::test_issue_comment ... ok
test cob::issue::test::test_issue_create_and_assign ... ok
test cob::issue::test::test_issue_comment_redact ... ok
test cob::issue::test::test_issue_create_and_change_state ... ok
test cob::issue::test::test_issue_create_and_get ... ok
test cob::issue::test::test_issue_create_and_unassign ... ok
test cob::issue::test::test_issue_create_and_reassign ... ok
test cob::issue::test::test_issue_edit ... ok
test cob::issue::test::test_issue_edit_description ... ok
test cob::issue::test::test_issue_multilines ... ok
test cob::issue::test::test_issue_state_serde ... ok
test cob::issue::test::test_ordering ... ok
test cob::patch::actions::test::test_review_edit ... ok
test cob::issue::test::test_issue_label ... ok
test cob::issue::test::test_issue_react ... ok
test cob::issue::test::test_issue_reply ... ok
test cob::patch::cache::tests::test_is_empty ... ok
test cob::patch::cache::tests::test_get ... ok
test cob::patch::cache::tests::test_list_by_status ... ok
test cob::patch::cache::tests::test_list ... ok
test cob::patch::encoding::review::test::test_review_deserialize_summary_migration_null_summary ... ok
test cob::patch::encoding::review::test::test_review_deserialize_summary_migration_with_summary ... ok
test cob::patch::encoding::review::test::test_review_deserialize_summary_migration_without_summary ... ok
test cob::patch::encoding::review::test::test_review_deserialize_summary_v2 ... ok
test cob::patch::encoding::review::test::test_review_summary ... ok
test cob::patch::test::test_json ... ok
test cob::patch::test::test_json_serialization ... ok
test cob::patch::test::test_patch_create_and_get ... ok
test cob::patch::cache::tests::test_remove ... ok
test cob::patch::cache::tests::test_counts ... ok
test cob::patch::test::test_patch_discussion ... ok
test cob::patch::test::test_patch_merge ... ok
test cob::patch::test::test_patch_review ... ok
test cob::patch::test::test_patch_redact ... ok
test cob::patch::test::test_patch_review_comment ... ok
test cob::patch::test::test_patch_review_duplicate ... ok
test cob::patch::test::test_patch_review_edit ... ok
test cob::patch::test::test_patch_review_edit_comment ... ok
test cob::patch::test::test_patch_review_remove_summary ... ok
test cob::patch::test::test_reactions_json_serialization ... ok
test cob::patch::test::test_revision_edit_redact ... ok
test cob::patch::test::test_revision_reaction ... ok
test cob::patch::test::test_revision_review_merge_redacted ... ok
test cob::stream::tests::test_all_from ... ok
test cob::patch::test::test_patch_review_revision_redact ... ok
test cob::stream::tests::test_all_from_until ... ok
test cob::stream::tests::test_all_until ... ok
test cob::stream::tests::test_from_until ... ok
test cob::stream::tests::test_regression_from_until ... ok
test cob::thread::tests::test_comment_edit_missing ... ok
test cob::patch::test::test_patch_update ... ok
test cob::thread::tests::test_comment_edit_redacted ... ok
test cob::thread::tests::test_comment_redact_missing ... ok
test cob::thread::tests::test_edit_comment ... ok
test cob::thread::tests::test_duplicate_comments ... ok
test cob::thread::tests::test_redact_comment ... ok
test git::canonical::protect::tests::refs_rad ... ok
test git::canonical::protect::tests::refs_rad_id ... ok
test git::canonical::protect::tests::refs_radieschen ... ok
test git::canonical::quorum::test::merge_base_commutative ... ok
test git::canonical::quorum::test::test_merge_bases ... ok
test cob::thread::tests::test_timeline ... ok
test git::canonical::rules::tests::test_deserialization ... ok
test git::canonical::rules::tests::test_deserialize_extensions ... ok
test git::canonical::rules::tests::test_order ... ok
test git::canonical::rules::tests::test_roundtrip ... ok
test git::canonical::rules::tests::test_canonical ... ok
test git::canonical::rules::tests::test_rule_validate_success ... ok
test git::canonical::rules::tests::test_special_branches ... ok
test git::canonical::symbolic::test::deserialize_infinite ... ok
test git::canonical::symbolic::test::deserialize_order ... ok
test git::canonical::symbolic::test::deserialize_valid ... ok
test git::canonical::symbolic::test::infinite_extend ... ok
test git::canonical::symbolic::test::infinite_multi ... ok
test git::canonical::symbolic::test::infinite_single ... ok
test git::canonical::symbolic::test::resolve_two_hop_chain ... ok
test git::canonical::tests::test_commit_quorum_fork_of_a_fork ... ok
test git::canonical::tests::test_commit_quorum_forked_merge_commits ... ok
test git::canonical::tests::test_commit_quorum_groups ... ok
test git::canonical::tests::test_commit_quorum_linear ... ok
test git::canonical::tests::test_commit_quorum_merges ... ok
test git::canonical::tests::test_commit_quorum_single ... ok
test git::canonical::tests::test_commit_quorum_three_way_fork ... ok
test git::canonical::tests::test_commit_quorum_two_way_fork ... ok
test git::canonical::tests::test_quorum_different_types ... ok
test git::canonical::rules::tests::test_rule_validate_failures ... ok
test git::canonical::tests::test_tag_quorum ... ok
test git::test::test_version_from_str ... ok
test git::test::test_version_ord ... ok
test identity::crefs::tests::invalid_clash ... ok
test identity::crefs::tests::invalid_clash_asterisk_name ... ok
test identity::crefs::tests::invalid_dangling ... ok
test identity::crefs::tests::omit_symbolic ... ok
test identity::crefs::tests::valid ... ok
test identity::crefs::tests::valid_asterisk_target ... ok
test identity::did::test::test_did_encode_decode ... ok
test identity::did::test::test_did_vectors ... ok
test identity::doc::test::default_branch_clash ... ok
test identity::doc::test::default_branch_without_project ... ok
test cob::patch::cache::tests::test_find_by_revision ... ok
test identity::doc::test::test_canonical_doc ... ok
test identity::doc::test::test_canonical_example ... ok
test identity::doc::test::test_duplicate_dids ... ok
test identity::doc::test::test_future_version_error ... ok
test identity::doc::test::test_is_valid_version ... ok
test identity::doc::test::test_max_delegates ... ok
test identity::doc::test::test_not_found ... ok
test identity::doc::test::test_parse_version ... ok
test identity::doc::test::test_visibility_json ... ok
test identity::doc::update::test::test_can_update_crefs ... ok
test identity::doc::update::test::test_cannot_include_default_branch_rule ... ok
test identity::doc::update::test::test_default_branch_rule_exists_after_verification ... ok
test identity::project::test::test_project_name ... ok
test node::address::store::test::test_alias ... ok
test node::address::store::test::test_disconnected ... ok
test node::address::store::test::test_disconnected_ban ... ok
test node::address::store::test::test_empty ... ok
test node::address::store::test::test_entries ... ok
test node::address::store::test::test_get_none ... ok
test node::address::store::test::test_insert_and_get ... ok
test node::address::store::test::test_insert_and_remove ... ok
test node::address::store::test::test_insert_and_update ... ok
test node::address::store::test::test_insert_duplicate ... ok
test node::address::store::test::test_node_aliases ... ok
test node::address::store::test::test_remove_nothing ... ok
test node::command::test::command_result ... ok
test node::config::test::deserialize_migrating_scope ... ok
test node::config::test::fetch_level_min ... ok
test node::config::test::onion_absent ... ok
test node::config::test::onion_null ... ok
test node::config::test::partial ... ok
test node::config::test::regression_ipv6_address_brackets ... ok
test node::config::test::regression_ipv6_address_no_brackets ... ok
test node::config::test::serialize_migrating_scope ... ok
test node::config::test::user_agent_custom ... ok
test node::config::test::user_agent_default ... ok
test node::config::test::user_agent_default_explicit ... ok
test node::config::test::user_agent_opt_out ... ok
test node::db::config::test::database_config_valid_combinations ... ok
test node::db::config::test::invalid ... ok
test node::db::test::migration_8::all_ipv6_formatted_dns_addresses_are_retyped ... ok
test node::db::test::migration_8::dns_address_starting_with_bracket_but_missing_closing_bracket_colon_is_unaffected ... ok
test node::db::test::migration_8::dns_address_with_bracket_not_at_start_is_unaffected ... ok
test node::db::test::migration_8::ipv4_address_is_unaffected ... ok
test node::db::test::migration_8::ipv6_formatted_dns_address_is_deleted_when_correct_ipv6_row_already_exists ... ok
test node::db::test::migration_8::ipv6_formatted_dns_address_is_retyped_to_ipv6 ... ok
test node::db::test::migration_8::migration_applies_to_all_nodes ... ok
test node::db::test::migration_8::plain_dns_hostname_without_brackets_is_unaffected ... ok
test node::db::test::migration_8::retype_preserves_address_metadata ... ok
test node::db::test::test_version ... ok
test node::features::test::test_operations ... ok
test node::notifications::store::test::test_branch_notifications ... ok
test node::notifications::store::test::test_clear ... ok
test node::notifications::store::test::test_cob_notifications ... ok
test node::notifications::store::test::test_counts_by_repo ... ok
test node::notifications::store::test::test_duplicate_notifications ... ok
test node::notifications::store::test::test_notification_status ... ok
test node::policy::store::test::test_follow_and_unfollow_node ... ok
test node::policy::store::test::test_node_aliases ... ok
test node::policy::store::test::test_node_policies ... ok
test node::policy::store::test::test_node_policy ... ok
test node::policy::store::test::test_repo_policies ... ok
test node::policy::store::test::test_repo_policy ... ok
test node::policy::store::test::test_seed_and_unseed_repo ... ok
test node::policy::store::test::test_update_alias ... ok
test node::policy::store::test::test_update_scope ... ok
test node::refs::store::test::test_count ... ok
test node::refs::store::test::test_set_and_delete ... ok
test node::refs::store::test::test_set_and_get ... ok
test node::routing::test::test_count ... ok
test node::routing::test::test_entries ... ok
test git::canonical::tests::test_quorum_properties ... ok
test node::routing::test::test_insert_and_get ... ok
test node::routing::test::test_insert_and_remove ... ok
test node::routing::test::test_insert_and_get_resources ... ok
test node::routing::test::test_insert_duplicate ... ok
test node::routing::test::test_insert_existing_updated_time ... ok
test node::routing::test::test_len ... ok
test node::routing::test::test_remove_many ... ok
test node::routing::test::test_remove_redundant ... ok
test node::routing::test::test_prune ... ok
test node::routing::test::test_update_existing_multi ... ok
test node::sync::announce::test::announcer_adapts_target_to_reach ... ok
test node::sync::announce::test::all_synced_nodes_are_preferred_seeds ... ok
test node::sync::announce::test::announcer_preferred_seeds_or_replica_factor ... ok
test node::sync::announce::test::announcer_reached_min_replication_target ... ok
test node::sync::announce::test::announcer_reached_max_replication_target ... ok
test node::sync::announce::test::announcer_synced_with_unknown_node ... ok
test node::sync::announce::test::announcer_reached_preferred_seeds ... ok
test node::sync::announce::test::announcer_timed_out ... ok
test node::sync::announce::test::announcer_with_replication_factor_zero_and_preferred_seeds ... ok
test node::sync::announce::test::construct_node_appears_in_multiple_input_sets ... ok
test node::sync::announce::test::cannot_construct_announcer ... ok
test node::sync::announce::test::construct_only_preferred_seeds_provided ... ok
test node::sync::announce::test::invariant_progress_should_match_state ... ok
test node::sync::announce::test::local_node_in_multiple_sets ... ok
test node::sync::announce::test::local_node_in_preferred_seeds ... ok
test node::sync::announce::test::local_node_in_synced_set ... ok
test node::sync::announce::test::local_node_only_in_all_sets_results_in_no_seeds_error ... ok
test node::sync::announce::test::local_node_in_unsynced_set ... ok
test node::sync::announce::test::synced_with_local_node_is_ignored ... ok
test cob::thread::tests::prop_ordering ... ok
test node::sync::announce::test::preferred_seeds_already_synced ... ok
test node::sync::announce::test::synced_with_same_node_multiple_times ... ok
test node::sync::announce::test::timed_out_after_reaching_success ... ok
test node::sync::fetch::test::could_not_reach_target ... ok
test node::sync::fetch::test::ignores_duplicates_and_local_node ... ok
test node::sync::fetch::test::all_nodes_are_candidates ... ok
test node::sync::fetch::test::preferred_seeds_target_returned_over_replicas ... ok
test node::sync::fetch::test::all_nodes_are_fetchable ... ok
test node::sync::fetch::test::reaches_target_of_max_replicas ... ok
test node::sync::test::ensure_replicas_construction ... ok
test node::sync::test::replicas_constrain_to ... ok
test node::test::test_address ... ok
test node::test::test_alias ... ok
test node::test::test_command_result ... ok
test node::test::test_user_agent ... ok
test node::timestamp::tests::test_timestamp_max ... ok
test node::sync::fetch::test::reaches_target_of_replicas ... ok
test node::sync::fetch::test::reaches_target_of_preferred_seeds ... ok
test profile::test::canonicalize_home ... ok
test profile::test::test_config ... ok
test rad::tests::test_checkout ... ok
test rad::tests::test_fork ... ok
test storage::git::tests::test_references_of ... ok
test rad::tests::test_init ... ok
test storage::git::transport::local::url::test::test_url_parse ... ok
test storage::git::transport::local::url::test::test_url_to_string ... ok
test storage::git::transport::remote::url::test::test_url_parse ... ok
test storage::git::tests::test_sign_refs ... ok
test profile::config::test::schema ... ok
test storage::refs::sigrefs::read::test::commit_reader::identity_root_error ... ok
test storage::refs::sigrefs::read::test::commit_reader::missing_commit ... ok
test storage::refs::sigrefs::read::test::commit_reader::read_ok ... ok
test storage::refs::sigrefs::read::test::commit_reader::too_many_parents ... ok
test storage::refs::sigrefs::read::test::commit_reader::tree_error ... ok
test storage::refs::sigrefs::read::test::identity_root_reader::doc_blob_error ... ok
test storage::refs::sigrefs::read::test::identity_root_reader::missing_identity ... ok
test storage::refs::sigrefs::read::test::identity_root_reader::read_ok_none ... ok
test storage::refs::sigrefs::read::test::identity_root_reader::read_ok_some ... ok
test storage::refs::sigrefs::read::test::resolve_tip::find_reference_error ... ok
test storage::refs::sigrefs::read::test::resolve_tip::missing_sigrefs ... ok
test storage::refs::sigrefs::read::test::resolve_tip::resolve_tip_ok ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::detect_parent::root_without_parent ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::detect_parent::root_without_root ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::downgrade::parent ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::downgrade::restore ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::downgrade::root ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::downgrade::root_with_parent ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::head_commit_error ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::head_verify_mismatched_identity_error ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::head_verify_signature_error ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::invalid_parent ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::read_ok_no_parent ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::read_ok_parent ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::read_ok_root ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::replay::alternating ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::replay::chain ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::replay::multiple ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::replay::root_at_head ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::single_commit ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::two_commits ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::walk_commit_error ... ok
test storage::refs::sigrefs::read::test::signed_refs_reader::walk_verify_error ... ok
test storage::refs::sigrefs::read::test::tree_reader::missing_both ... ok
test storage::refs::sigrefs::read::test::tree_reader::missing_refs ... ok
test storage::refs::sigrefs::read::test::tree_reader::missing_signature ... ok
test storage::refs::sigrefs::read::test::tree_reader::parse_refs_error ... ok
test storage::refs::sigrefs::read::test::tree_reader::parse_signature_error ... ok
test storage::refs::sigrefs::read::test::tree_reader::read_ok ... ok
test storage::refs::sigrefs::read::test::tree_reader::read_refs_error ... ok
test storage::refs::sigrefs::read::test::tree_reader::read_signature_error ... ok
test storage::refs::sigrefs::write::test::commit_writer::tree_error ... ok
test storage::refs::sigrefs::write::test::commit_writer::write_commit_error ... ok
test storage::refs::sigrefs::write::test::commit_writer::write_empty_refs ... ok
test storage::refs::sigrefs::write::test::commit_writer::write_root_ok ... ok
test storage::refs::sigrefs::write::test::commit_writer::write_with_parent_ok ... ok
test storage::refs::sigrefs::write::test::head_reader::no_head ... ok
test storage::refs::sigrefs::write::test::head_reader::read_ok ... ok
test storage::refs::sigrefs::write::test::head_reader::reference_error ... ok
test storage::refs::sigrefs::write::test::head_reader::refs_blob_error ... ok
test storage::refs::sigrefs::write::test::head_reader::refs_blob_missing ... ok
test storage::refs::sigrefs::write::test::head_reader::refs_parse_error ... ok
test storage::refs::sigrefs::write::test::head_reader::signature_blob_error ... ok
test storage::refs::sigrefs::write::test::head_reader::signature_blob_missing ... ok
test storage::refs::sigrefs::write::test::head_reader::signature_parse_error ... ok
test storage::refs::sigrefs::write::test::signed_refs_writer::commit_error ... ok
test storage::refs::sigrefs::write::test::signed_refs_writer::head_error ... ok
test storage::refs::sigrefs::write::test::signed_refs_writer::never_write_rad_sigrefs ... ok
test storage::refs::sigrefs::write::test::signed_refs_writer::reference_error ... ok
test storage::refs::sigrefs::write::test::signed_refs_writer::unchanged ... ok
test storage::refs::sigrefs::write::test::signed_refs_writer::unchanged_force_writes_new_commit ... ok
test storage::refs::sigrefs::write::test::signed_refs_writer::write_empty_refs ... ok
test storage::refs::sigrefs::write::test::signed_refs_writer::write_root_ok ... ok
test storage::refs::sigrefs::write::test::signed_refs_writer::write_with_parent_ok ... ok
test storage::refs::sigrefs::write::test::tree_writer::sign_error ... ok
test storage::refs::sigrefs::write::test::tree_writer::write_ok ... ok
test storage::refs::sigrefs::write::test::tree_writer::write_tree_error ... ok
test storage::refs::tests::prop_canonical_roundtrip ... ok
test storage::refs::tests::test_rid_verification ... ok
test storage::tests::test_storage ... ok
test test::assert::test::assert_with_message ... ok
test test::assert::test::test_assert_no_move ... ok
test test::assert::test::test_assert_panic_0 - should panic ... ok
test test::assert::test::test_assert_panic_1 - should panic ... ok
test test::assert::test::test_assert_panic_2 - should panic ... ok
test test::assert::test::test_assert_succeed ... ok
test test::assert::test::test_panic_message ... ok
test version::test::test_version ... ok
test storage::refs::sigrefs::property::idempotent ... ok
test identity::doc::test::prop_encode_decode ... ok
test storage::refs::sigrefs::property::roundtrip ... ok
test result: ok. 345 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 3.41s
Running unittests src/lib.rs (target/debug/deps/radicle_cli-892456d4d760068b)
running 46 tests
test commands::block::args::test::should_parse_nid ... ok
test commands::block::args::test::should_not_parse ... ok
test commands::clone::args::test::should_parse_rid_non_urn ... ok
test commands::block::args::test::should_parse_rid ... ok
test commands::clone::args::test::should_parse_rid_url ... ok
test commands::clone::args::test::should_parse_rid_urn ... ok
test commands::cob::args::test::should_allow_log_json_format ... ok
test commands::cob::args::test::should_allow_log_pretty_format ... ok
test commands::cob::args::test::should_not_allow_show_pretty_format ... ok
test commands::cob::args::test::should_allow_update_json_format ... ok
test commands::fork::args::test::should_not_parse_rid_url ... ok
test commands::fork::args::test::should_parse_rid_non_urn ... ok
test commands::cob::args::test::should_not_allow_update_pretty_format ... ok
test commands::fork::args::test::should_parse_rid_urn ... ok
test commands::cob::args::test::should_allow_show_json_format ... ok
test commands::id::args::test::should_not_clobber_payload_args ... ok
test commands::id::args::test::should_not_parse_into_payload - should panic ... ok
test commands::id::args::test::should_parse_into_payload ... ok
test commands::id::args::test::should_not_parse_single_payloads ... ok
test commands::id::args::test::should_not_parse_single_payload ... ok
test commands::init::args::test::should_not_parse_rid_url ... ok
test commands::id::args::test::should_parse_single_payload ... ok
test commands::id::args::test::should_parse_multiple_payloads ... ok
test commands::init::args::test::should_parse_rid_non_urn ... ok
test commands::patch::review::builder::tests::test_review_comments_basic ... ok
test commands::inspect::test::test_tree ... ok
test commands::patch::review::builder::tests::test_review_comments_before ... ok
test commands::init::args::test::should_parse_rid_urn ... ok
test commands::patch::review::builder::tests::test_review_comments_split_hunk ... ok
test commands::patch::review::builder::tests::test_review_comments_multiline ... ok
test commands::publish::args::test::should_not_parse_rid_url ... ok
test git::ddiff::tests::diff_encode_decode_ddiff_hunk ... ok
test commands::publish::args::test::should_parse_rid_urn ... ok
test commands::publish::args::test::should_parse_rid_non_urn ... ok
test git::pretty_diff::test::test_pretty ... ignored
test commands::watch::args::test::should_parse_ref_str ... ok
test terminal::args::test::should_not_parse ... ok
test git::unified_diff::test::test_diff_content_encode_decode_content ... ok
test terminal::args::test::should_parse_nid ... ok
test git::unified_diff::test::test_diff_encode_decode_diff ... ok
test terminal::format::test::test_bytes ... ok
test terminal::format::test::test_strip_comments ... ok
test terminal::patch::test::test_edit_display_message ... ok
test terminal::args::test::should_parse_rid ... ok
test terminal::patch::test::test_create_display_message ... ok
test terminal::patch::test::test_update_display_message ... ok
test result: ok. 45 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.01s
Running unittests src/main.rs (target/debug/deps/rad-eabed658f25948db)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running tests/commands.rs (target/debug/deps/commands-66a4c4c504183aa2)
running 115 tests
test commands::clone::rad_clone ... ok
test commands::clone::rad_clone_bare ... ok
test commands::checkout::rad_checkout ... ok
test commands::clone::rad_clone_all ... ok
test commands::clone::rad_clone_scope ... ok
test commands::clone::rad_clone_unknown ... ok
test commands::clone::rad_clone_directory ... ok
test commands::clone::rad_clone_connect ... ok
test commands::clone::rad_clone_partial_fail ... ok
test commands::cob::rad_cob_multiset ... ok
test commands::clone::test_clone_without_seeds ... ok
test commands::cob::rad_cob_log ... ok
test commands::cob::rad_cob_migrate ... ok
test commands::cob::rad_cob_operations ... ok
test commands::cob::rad_cob_show ... ok
test commands::cob::rad_cob_update_identity ... ok
test commands::cob::rad_cob_update ... ok
test commands::cob::test_cob_deletion ... ok
test commands::cob::test_cob_replication ... ok
test commands::git::git_push_amend ... ok
test commands::git::git_push_and_fetch ... ok
test commands::git::git_push_canonical_lightweight_tags ... ok
test commands::git::git_push_diverge ... ok
test commands::git::git_push_force_with_lease ... ok
test commands::git::git_push_canonical ... ok
test commands::git::git_push_converge ... ok
test commands::id::rad_id_collaboration ... ignored, slow
test commands::id::rad_id ... ok
test commands::git::git_tag ... ok
test commands::git::git_push_rollback ... ok
test commands::id::rad_id_private ... ok
test commands::id::rad_id_threshold_soft_fork ... ok
test commands::id::rad_id_conflict ... ok
test commands::id::rad_id_threshold ... ok
test commands::id::rad_id_unknown_field ... ok
test commands::id::rad_id_update_delete_field ... ok
test commands::init::rad_init ... ignored, part of many other tests
test commands::id::rad_id_unauthorized_delegate ... ok
test commands::id::rad_id_multi_delegate ... ok
test commands::init::rad_init_bare ... ok
test commands::init::rad_init_detached_head ... ok
test commands::init::rad_init_no_git ... ok
test commands::init::rad_init_existing ... ok
test commands::init::rad_init_existing_bare ... ok
test commands::init::rad_init_no_seed ... ok
test commands::init::rad_init_private ... ok
test commands::init::rad_init_private_no_seed ... ok
test commands::init::rad_init_private_clone ... ok
test commands::inbox::rad_inbox ... ok
test commands::init::rad_init_private_clone_seed ... ok
test commands::init::rad_init_private_seed ... ok
test commands::init::rad_init_sync_not_connected ... ok
test commands::init::rad_init_sync_preferred ... ok
test commands::init::rad_init_with_existing_remote ... ok
test commands::init::rad_publish ... ok
test commands::issue::rad_issue ... ok
test commands::jj::rad_jj_bare ... ok
test commands::jj::rad_jj_colocated_patch ... ok
test commands::issue::rad_issue_list ... ok
test commands::node::rad_node_connect ... ok
test commands::node::rad_node_connect_without_address ... ok
test commands::node::rad_node ... ok
test commands::patch::rad_merge_after_update ... ok
test commands::patch::rad_merge_no_ff ... ok
test commands::patch::rad_merge_via_push ... ok
test commands::patch::rad_patch ... ok
test commands::patch::rad_patch_ahead_behind ... ok
test commands::patch::rad_patch_change_base ... ok
test commands::patch::rad_patch_checkout ... ok
test commands::init::rad_init_sync_timeout ... ok
test commands::init::rad_init_sync_and_clone ... ok
test commands::patch::rad_patch_checkout_revision ... ok
test commands::patch::rad_patch_detached_head ... ok
test commands::patch::rad_patch_diff ... ok
test commands::patch::rad_patch_checkout_force ... ok
test commands::patch::rad_patch_draft ... ok
test commands::patch::rad_patch_edit ... ok
test commands::patch::rad_patch_fetch_2 ... FAILED
test commands::patch::rad_patch_fetch_1 ... ok
test commands::patch::rad_patch_merge_draft ... ok
test commands::patch::rad_patch_revert_merge ... ok
test commands::patch::rad_patch_delete ... ok
test commands::patch::rad_patch_open_explore ... ok
test commands::patch::rad_patch_via_push ... FAILED
test commands::patch::rad_patch_update ... ok
test commands::policy::rad_block ... ok
test commands::policy::rad_seed_and_follow ... ok
test commands::patch::rad_review_by_hunk ... ok
test commands::policy::rad_seed_policy_allow_no_scope ... ok
test commands::policy::rad_seed_scope ... ok
test commands::policy::rad_unseed ... ok
test commands::policy::rad_seed_many ... ok
test commands::policy::rad_unseed_many ... ok
test commands::patch::rad_push_and_pull_patches ... ok
test commands::remote::rad_remote ... ok
test commands::sync::rad_sync_without_node ... ok
test commands::sync::rad_sync ... ok
test commands::utility::framework_home ... ok
test commands::utility::rad_auth ... ok
test commands::utility::rad_auth_errors ... ok
test commands::patch::rad_patch_pull_update ... ok
test commands::utility::rad_clean ... ok
test commands::utility::rad_config ... ok
test commands::utility::rad_diff ... ok
test commands::utility::rad_help ... ok
test commands::utility::rad_inspect ... ok
test commands::utility::rad_key_mismatch ... ok
test commands::utility::rad_self ... ok
test commands::utility::rad_warn_old_nodes ... ok
test commands::sync::rad_fetch ... ok
test commands::watch::rad_watch ... ok
test rad_remote ... ok
test commands::sync::test_replication_via_seed ... ok
test commands::workflow::rad_workflow ... ok
test commands::utility::rad_fork ... ok
failures:
---- commands::patch::rad_patch_fetch_2 stdout ----
1776332033 test: rad-init:6: `rad init --name heartwood --description Radicle Heartwood Protocol & Stack --no-confirm --public -v` @ /tmp/.tmpWrgZ1D/alice/work
1776332033 test: rad-init:28: `rad init` @ /tmp/.tmpWrgZ1D/alice/work
1776332033 test: rad-init:35: `rad ls` @ /tmp/.tmpWrgZ1D/alice/work
1776332033 test: rad-init:46: `rad node inventory` @ /tmp/.tmpWrgZ1D/alice/work
1776332033 test: rad-patch-fetch-2:6: `git checkout -b alice/1 -q` @ /tmp/.tmpWrgZ1D/alice/work
1776332033 test: rad-patch-fetch-2:7: `git commit --allow-empty -m Changes #1 -q` @ /tmp/.tmpWrgZ1D/alice/work
1776332033 test: rad-patch-fetch-2:8: `git push rad -o patch.message=Changes HEAD:refs/patches` @ /tmp/.tmpWrgZ1D/alice/work
1776332034 test: rad-patch-fetch-2:12: `git checkout master -q` @ /tmp/.tmpWrgZ1D/alice/work
1776332034 test: rad-patch-fetch-2:13: `git branch -D alice/1 -q` @ /tmp/.tmpWrgZ1D/alice/work
1776332034 test: rad-patch-fetch-2:14: `git update-ref -d refs/remotes/rad/alice/1` @ /tmp/.tmpWrgZ1D/alice/work
1776332034 test: rad-patch-fetch-2:15: `git update-ref -d refs/remotes/rad/patches/5e2dedcc5d515fcbc1cca483d3376609fe889bfb` @ /tmp/.tmpWrgZ1D/alice/work
1776332034 test: rad-patch-fetch-2:16: `git gc --prune=now` @ /tmp/.tmpWrgZ1D/alice/work
1776332034 test: rad-patch-fetch-2:17: `git branch -r` @ /tmp/.tmpWrgZ1D/alice/work
1776332034 test: rad-patch-fetch-2:22: `git pull` @ /tmp/.tmpWrgZ1D/alice/work
1776332034 test: rad-patch-fetch-2:24: `git branch -r` @ /tmp/.tmpWrgZ1D/alice/work
thread 'commands::patch::rad_patch_fetch_2' panicked at crates/radicle-cli-test/src/lib.rs:500:36:
--- Expected
++++ actual: stdout
1 - rad/HEAD -> rad/master
2 1 | rad/master
3 2 | rad/patches/5e2dedcc5d515fcbc1cca483d3376609fe889bfb
Exit status: 0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---- commands::patch::rad_patch_via_push stdout ----
1776332035 test: rad-init:6: `rad init --name heartwood --description Radicle Heartwood Protocol & Stack --no-confirm --public -v` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-init:28: `rad init` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-init:35: `rad ls` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-init:46: `rad node inventory` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-patch-via-push:7: `git checkout -b feature/1` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-patch-via-push:9: `git commit -a -m Add things -q --allow-empty` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-patch-via-push:10: `git push -o patch.message=Add things #1 -o patch.message=See commits for details. rad HEAD:refs/patches` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-patch-via-push:22: `rad patch show 6035d2f582afbe01ff23ea87528ae523d76875b6` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-patch-via-push:45: `git branch -vv` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-patch-via-push:53: `git status --short --branch` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-patch-via-push:55: `git fetch` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-patch-via-push:56: `git push` @ /tmp/.tmpw2CrcV/alice/work
1776332035 test: rad-patch-via-push:62: `git show-ref` @ /tmp/.tmpw2CrcV/alice/work
thread 'commands::patch::rad_patch_via_push' panicked at crates/radicle-cli-test/src/lib.rs:500:36:
--- Expected
++++ actual: stdout
1 1 | 42d894a83c9c356552a57af09ccdbd5587a99045 refs/heads/feature/1
2 2 | f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
3 - f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/remotes/rad/HEAD
4 3 | f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/remotes/rad/master
5 4 | 42d894a83c9c356552a57af09ccdbd5587a99045 refs/remotes/rad/patches/6035d2f582afbe01ff23ea87528ae523d76875b6
Exit status: 0
failures:
commands::patch::rad_patch_fetch_2
commands::patch::rad_patch_via_push
test result: FAILED. 111 passed; 2 failed; 2 ignored; 0 measured; 0 filtered out; finished in 74.33s
error: test failed, to rerun pass `-p radicle-cli --test commands`
Running unittests src/lib.rs (target/debug/deps/radicle_cli_test-33c65bc5b73aa9c8)
running 3 tests
test tests::test_parse ... ok
test tests::test_run ... ok
test tests::test_example_spaced_brackets ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_cob-6fe139a7978b5280)
running 9 tests
test object::tests::test_serde ... ok
test tests::git::roundtrip ... ok
test tests::invalid_parse_refstr ... ok
test tests::git::update_cob ... ok
test type_name::test::invalid_typenames ... ok
test type_name::test::valid_typenames ... ok
test tests::git::list_cobs ... ok
test tests::parse_refstr ... ok
test tests::git::traverse_cobs ... ok
test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s
Running unittests src/lib.rs (target/debug/deps/radicle_core-e6bfed2c189a2d2c)
running 4 tests
test repo::test::valid ... ok
test repo::test::invalid ... ok
test repo::test::assert_prop_roundtrip_parse ... ok
test repo::serde_impls::test::assert_prop_roundtrip_serde_json ... ok
test result: ok. 4 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_crypto-99d51a8f68c92ad6)
running 11 tests
test ssh::agent::test::test_agent_encoding_remove ... ok
test ssh::agent::test::test_agent_encoding_sign ... ok
test ssh::fmt::test::test_key ... ok
test ssh::fmt::test::test_fingerprint ... ok
test ssh::keystore::tests::test_init_no_passphrase ... ok
test tests::prop_encode_decode ... ok
test tests::test_e25519_dh ... ok
test tests::test_encode_decode ... ok
test tests::prop_key_equality ... ok
test ssh::keystore::tests::test_signer ... ok
test ssh::keystore::tests::test_init_passphrase ... ok
test result: ok. 11 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.85s
Running unittests src/lib.rs (target/debug/deps/radicle_dag-05f7457e7194a495)
running 20 tests
test tests::test_contains ... ok
test tests::test_dependencies ... ok
test tests::test_cycle ... ok
test tests::test_diamond ... ok
test tests::test_fold_multiple_roots ... ok
test tests::test_fold_reject ... ok
test tests::test_fold_diamond ... ok
test tests::test_fold_sorting_1 ... ok
test tests::test_get ... ok
test tests::test_complex ... ok
test tests::test_fold_sorting_2 ... ok
test tests::test_len ... ok
test tests::test_is_empty ... ok
test tests::test_merge_1 ... ok
test tests::test_merge_2 ... ok
test tests::test_prune_1 ... ok
test tests::test_remove ... ok
test tests::test_prune_2 ... ok
test tests::test_siblings ... ok
test tests::test_prune_by_sorting ... ok
test result: ok. 20 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_fetch-6dc08dcd25bc0cab)
running 1 test
test stage::test::valid_refspecs ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_git_metadata-7266929c438027dc)
running 24 tests
test commit::parse::test::error::invalid_author ... ok
test commit::parse::test::error::invalid_committer ... ok
test commit::parse::test::error::invalid_tree ... ok
test commit::parse::test::error::invalid_parent ... ok
test commit::parse::test::error::missing_author ... ok
test commit::parse::test::error::invalid_format_continuation_without_preceding_header ... ok
test commit::parse::test::error::missing_header_body_separator ... ok
test commit::parse::test::error::missing_committer ... ok
test commit::parse::test::error::missing_tree_empty_header ... ok
test commit::parse::test::success::commit_last_paragraph_kept_in_message_when_not_all_trailers ... ok
test commit::parse::test::error::missing_tree_wrong_first_line ... ok
test commit::parse::test::success::commit_with_extra_headers ... ok
test commit::parse::test::success::commit_gpgsig_is_preserved_and_strip_removes_it ... ok
test commit::parse::test::success::commit_with_multiline_gpgsig ... ok
test commit::parse::test::success::root_commit ... ok
test commit::parse::test::success::commit_with_single_parent ... ok
test commit::parse::test::success::merge_commit ... ok
test commit::parse::test::success::commit_with_trailers ... ok
test commit::parse::test::success::roundtrip ... ok
test commit::parse::test::unit::trailers_rejects_invalid_token_chars ... ok
test commit::parse::test::unit::body_last_paragraph_not_trailers_stays_in_message ... ok
test commit::parse::test::unit::body_no_paragraph_separator_means_no_trailers ... ok
test commit::parse::test::unit::trailers_accepts_empty_input ... ok
test commit::parse::test::unit::trailers_rejects_line_without_separator ... ok
test result: ok. 24 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_git_ref_format-b95a9bd0637298c8)
running 9 tests
test test::component ... ok
test test::pattern ... ok
test test::component_invalid - should panic ... ok
test test::qualified ... ok
test test::qualified_invalid - should panic ... ok
test test::qualified_pattern ... ok
test test::qualified_pattern_invalid - should panic ... ok
test test::refname ... ok
test test::refname_invalid - should panic ... ok
test result: ok. 9 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_localtime-9ecb37b1e50cb4e3)
running 1 test
test serde_impls::test::test_localtime ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_node-62029d2b2a3d96c6)
running 79 tests
test reactor::timer::tests::test_next ... ok
test reactor::timer::tests::test_wake ... ok
test reactor::timer::tests::test_wake_exact ... ok
test control::tests::test_control_socket ... ok
test control::tests::test_seed_unseed ... ok
test fingerprint::tests::matching ... ok
test tests::e2e::missing_default_branch ... ok
test tests::e2e::fetch_does_not_contain_rad_sigrefs_parent ... ok
test tests::e2e::missing_delegate_default_branch ... ok
test tests::e2e::test_block_prevents_connection ... ok
test tests::e2e::test_background_foreground_fetch ... ok
test tests::e2e::test_block_active_connection ... ok
test tests::e2e::test_block_prevents_fetch ... ok
test tests::e2e::test_channel_reader_limit ... ok
test tests::e2e::test_catchup_on_refs_announcements ... ok
test tests::e2e::test_clone ... ok
test tests::e2e::test_dont_fetch_owned_refs ... ok
test tests::e2e::test_connection_crossing ... ok
test tests::e2e::test_fetch_followed_remotes ... ok
test tests::e2e::test_concurrent_fetches ... ok
test tests::e2e::test_fetch_unseeded ... ok
test tests::e2e::test_fetch_preserve_owned_refs ... ok
test tests::e2e::test_fetch_up_to_date ... ok
test tests::e2e::test_fetch_emits_canonical_ref_update ... ok
test tests::e2e::test_inventory_sync_basic ... ok
test tests::e2e::test_large_fetch ... ok
test tests::e2e::test_migrated_clone ... ok
test tests::e2e::test_missing_remote ... ok
test tests::e2e::test_multiple_offline_inits ... ok
test tests::e2e::test_non_fastforward_identity_doc ... ok
test tests::e2e::test_non_fastforward_sigrefs ... ok
test tests::e2e::test_outdated_delegate_sigrefs ... ok
test tests::e2e::test_outdated_sigrefs ... ok
test tests::e2e::test_replication ... ok
test tests::e2e::test_replication_invalid ... ok
test tests::e2e::test_inventory_sync_bridge ... ok
test tests::e2e::test_inventory_sync_ring ... ok
test tests::e2e::test_replication_ref_in_sigrefs ... ok
test tests::test_announcement_rebroadcast ... ok
test tests::e2e::test_inventory_sync_star ... ok
test tests::test_announcement_rebroadcast_duplicates ... ok
test tests::test_announcement_rebroadcast_timestamp_filtered ... ok
test tests::test_connection_kept_alive ... ok
test tests::test_announcement_relay ... ok
test tests::test_disconnecting_unresponsive_peer ... ok
test tests::test_fetch_missing_inventory_on_gossip ... ok
test tests::test_fetch_missing_inventory_on_schedule ... ok
test tests::test_inbound_connection ... ok
test tests::test_inventory_decode ... ok
test tests::test_init_and_seed ... ok
test tests::test_inventory_relay ... ok
test tests::test_inventory_relay_bad_timestamp ... ok
test tests::test_inventory_sync ... ok
test tests::test_maintain_connections ... ok
test tests::test_maintain_connections_failed_attempt ... ok
test tests::test_maintain_connections_transient ... ok
test tests::test_outbound_connection ... ok
test tests::test_inventory_pruning ... ok
test tests::test_persistent_peer_connect ... ok
test tests::test_persistent_peer_reconnect_attempt ... ok
test tests::test_persistent_peer_reconnect_success ... ok
test tests::test_ping_response ... ok
test tests::test_queued_fetch_from_ann_same_rid ... ok
test tests::test_queued_fetch_max_capacity ... ok
test tests::test_queued_fetch_from_command_same_rid ... ok
test tests::test_redundant_connect ... ok
test tests::test_refs_announcement_fetch_trusted_no_inventory ... ok
test tests::test_refs_announcement_followed ... ok
test tests::test_refs_announcement_no_subscribe ... ok
test tests::test_refs_announcement_offline ... ok
test tests::prop_inventory_exchange_dense ... ok
test tests::test_refs_announcement_relay_private ... ok
test tests::test_refs_synced_event ... ok
test tests::test_announcement_message_amplification ... ok
test wire::test::test_inventory_ann_with_extension ... ok
test wire::test::test_pong_message_with_extension ... ok
test tests::test_seed_repo_subscribe ... ok
test tests::test_refs_announcement_relay_public ... ok
test tests::test_seeding ... ok
test result: ok. 79 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 15.05s
Running unittests src/main.rs (target/debug/deps/radicle_node-12b1e07c8a70f4fc)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_oid-32b41d88203b7116)
running 10 tests
test fmt::test::fixture ... ok
test fmt::test::zero ... ok
test git2::test::zero ... ok
test gix::test::zero ... ok
test str::test::fixture ... ok
test fmt::test::git2 ... ok
test fmt::test::gix ... ok
test str::test::zero ... ok
test str::test::gix_roundrip ... ok
test str::test::git2_roundtrip ... ok
test result: ok. 10 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_protocol-4b43f12047c83efa)
running 99 tests
test deserializer::test::test_decode_next ... ok
test deserializer::test::test_unparsed ... ok
test deserializer::test::prop_decode_next ... ok
test fetcher::service::tests::test_fetch_coalescing_different_refs ... ok
test fetcher::test::queue::properties::capacity::bounded ... ok
test fetcher::test::queue::properties::capacity::rejection ... ok
test fetcher::test::queue::properties::dequeue::empty_queue_returns_none ... ok
test fetcher::test::queue::properties::dequeue::enables_reenqueue ... ok
test fetcher::test::queue::properties::dequeue::drained_queue_returns_none ... ok
test fetcher::test::queue::properties::capacity::capacity_reached_returns_same_item ... ok
test fetcher::test::queue::properties::capacity::restored_after_dequeue ... ok
test fetcher::test::queue::properties::fifo::interleaved_operations ... ok
test fetcher::test::queue::properties::fifo::ordering ... ok
test fetcher::test::queue::properties::equality::reflexive ... ok
test fetcher::test::queue::properties::equality::symmetric ... ok
test fetcher::test::queue::properties::merge::different_rid_accepted ... ok
test fetcher::test::queue::properties::merge::combines_refs ... ok
test fetcher::test::queue::properties::merge::longer_timeout_preserved ... ok
test fetcher::test::queue::properties::equality::transitive ... ok
test fetcher::test::queue::properties::merge::empty_refs_fetches_all ... ok
test fetcher::test::queue::unit::capacity_takes_precedence_over_merge_for_new_items ... ok
test fetcher::test::queue::unit::empty_refs_items_can_be_equal ... ok
test fetcher::test::queue::unit::max_timeout_accepted ... ok
test fetcher::test::queue::unit::merge_preserves_position_in_queue ... ok
test fetcher::test::queue::unit::zero_timeout_accepted ... ok
test fetcher::test::queue::properties::merge::does_not_increase_queue_length ... ok
test fetcher::test::state::command::cancel::non_existent_returns_unexpected ... ok
test fetcher::test::state::command::cancel::ongoing_and_queued ... ok
test fetcher::test::state::command::cancel::cancellation_is_isolated ... ok
test fetcher::test::state::command::cancel::single_ongoing ... ok
test fetcher::test::state::command::fetch::fetch_after_previous_completed ... ok
test fetcher::test::state::command::fetch::fetch_at_capacity_enqueues ... ok
test fetcher::test::state::command::fetch::fetch_different_repo_same_node_within_capacity ... ok
test fetcher::test::state::command::fetch::fetch_duplicate_returns_already_fetching ... ok
test fetcher::test::state::command::fetch::fetch_queue_merge_empty_refs_fetches_all ... ok
test fetcher::test::state::command::fetch::fetch_queue_merge_takes_longer_timeout ... ok
test fetcher::test::state::command::fetch::fetch_queue_merges_already_queued ... ok
test fetcher::test::state::command::fetch::fetch_same_repo_different_nodes_queues_second ... ok
test fetcher::test::state::command::fetch::fetch_queue_rejected_capacity_reached ... ok
test fetcher::test::state::command::fetch::fetch_same_repo_different_refs_enqueues ... ok
test fetcher::test::state::command::fetch::fetch_start_first_fetch_for_node ... ok
test fetcher::test::state::command::fetched::complete_single_ongoing ... ok
test fetcher::test::state::command::fetched::complete_one_of_multiple ... ok
test fetcher::test::state::command::fetched::non_existent_returns_not_found ... ok
test fetcher::test::state::concurrent::fetched_then_cancel ... ok
test fetcher::test::state::concurrent::interleaved_operations ... ok
test fetcher::test::state::command::fetched::complete_then_dequeue_fifo ... ok
test fetcher::test::state::config::min_queue_size ... ok
test fetcher::test::state::dequeue::cannot_dequeue_while_node_at_capacity ... ok
test fetcher::test::state::dequeue::empty_queue_returns_none ... ok
test fetcher::test::state::dequeue::maintains_fifo_order ... ok
test fetcher::test::queue::properties::merge::succeed_when_at_capacity ... ok
test fetcher::test::state::invariant::queue_integrity_after_merge ... ok
test fetcher::test::state::multinode::independent_queues ... ok
test service::filter::test::compatible ... ok
test service::filter::test::test_parameters ... ok
test service::filter::test::test_sizes ... ok
test service::gossip::store::test::test_announced ... ok
test service::limiter::test::test_limiter_different_rates ... ok
test service::limiter::test::test_limiter_multi ... ok
test service::limiter::test::test_limiter_refill ... ok
test fetcher::test::queue::properties::merge::same_rid_merges_anywhere_in_queue ... ok
test fetcher::test::state::config::high_concurrency ... ok
test service::message::tests::test_inventory_limit ... ok
test service::message::tests::test_ref_remote_limit ... ok
test wire::frame::test::test_encode_git_large ... ok
test wire::frame::test::test_stream_id ... ok
test fetcher::test::state::multinode::high_count ... ok
test wire::message::tests::prop_roundtrip_address ... ok
test service::message::tests::prop_refs_announcement_signing ... ok
test wire::message::tests::prop_zero_bytes_encode_decode ... ok
test wire::message::tests::test_inv_ann_max_size ... ok
test wire::message::tests::test_node_ann_max_size ... ok
test wire::message::tests::test_ping_encode_size_overflow - should panic ... ok
test wire::message::tests::test_pingpong_encode_max_size ... ok
test wire::message::tests::test_pong_encode_size_overflow - should panic ... ok
test service::message::tests::test_node_announcement_validate ... ok
test wire::tests::prop_oid ... ok
test wire::tests::prop_roundtrip_filter ... ok
test wire::tests::prop_roundtrip_publickey ... ok
test wire::tests::prop_roundtrip_refs ... ok
test wire::tests::prop_roundtrip_repoid ... ok
test wire::tests::prop_roundtrip_tuple ... ok
test wire::tests::prop_roundtrip_u16 ... ok
test wire::tests::prop_roundtrip_u32 ... ok
test wire::tests::prop_roundtrip_u64 ... ok
test wire::tests::prop_roundtrip_vec ... ok
test wire::tests::prop_signature ... ok
test wire::tests::prop_string ... ok
test wire::tests::test_alias ... ok
test wire::tests::test_bounded_vec_limit ... ok
test wire::tests::test_filter_invalid ... ok
test wire::tests::test_string ... ok
test wire::varint::test::prop_roundtrip_varint ... ok
test wire::varint::test::test_encode_overflow - should panic ... ok
test wire::varint::test::test_encoding ... ok
test wire::message::tests::prop_roundtrip_message ... ok
test wire::message::tests::test_refs_ann_max_size ... ok
test wire::message::tests::prop_message_decoder ... ok
test result: ok. 99 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.88s
Running unittests src/main.rs (target/debug/deps/git_remote_rad-3d1f3e21a25a9c1e)
running 12 tests
test protocol::tests::test_fetch_whitespace ... ok
test protocol::tests::test_capabilities ... ok
test protocol::tests::test_empty ... ok
test protocol::tests::test_fetch ... ok
test protocol::tests::test_invalid ... ok
test protocol::tests::test_list_for_push ... ok
test protocol::tests::test_list ... ok
test protocol::tests::test_option ... ok
test protocol::tests::test_option_whitespace_preservation ... ok
test protocol::tests::test_push ... ok
test protocol::tests::test_push_delete ... ok
test protocol::tests::test_push_force ... ok
test result: ok. 12 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/main.rs (target/debug/deps/radicle_schemars-8b40fe7336a27890)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_signals-edd60ee35e85748c)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_systemd-d0fb15a14ac3d4e9)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_term-51db4123a6bc434a)
running 21 tests
test ansi::tests::colors_disabled ... ok
test cell::test::test_width ... ok
test ansi::tests::colors_enabled ... ok
test element::test::test_spaced ... ok
test element::test::test_width ... ok
test table::test::test_table ... ok
test element::test::test_truncate ... ok
test ansi::tests::wrapping ... ok
test table::test::test_table_border_truncated ... ok
test table::test::test_table_border_maximized ... ok
test table::test::test_table_truncate ... ok
test table::test::test_table_unicode ... ok
test table::test::test_table_border ... ok
test table::test::test_table_unicode_truncate ... ok
test textarea::test::test_wrapping ... ok
test table::test::test_truncate ... ok
test vstack::test::test_vstack ... ok
test textarea::test::test_wrapping_code_block ... ok
test textarea::test::test_wrapping_fenced_block ... ok
test textarea::test::test_wrapping_paragraphs ... ok
test vstack::test::test_vstack_maximize ... ok
test result: ok. 21 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running unittests src/lib.rs (target/debug/deps/radicle_windows-2c067555aa9e0165)
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle
running 1 test
test crates/radicle/src/cob/patch/encoding/review.rs - cob::patch::encoding::review::Review (line 23) ... ignored
test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_cli
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_cli_test
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_cob
running 1 test
test crates/radicle-cob/src/backend/stable.rs - backend::stable::with_advanced_timestamp (line 56) ... ignored
test result: ok. 0 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_core
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_crypto
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_dag
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_fetch
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_git_metadata
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_git_ref_format
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_localtime
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_node
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_oid
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_protocol
running 6 tests
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::push (line 122) ... ok
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::max (line 96) ... ok
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::unbound (line 149) ... ok
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::collect_from (line 30) ... ok
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::truncate (line 50) ... ok
test crates/radicle-protocol/src/bounded.rs - bounded::BoundedVec<T,N>::with_capacity (line 66) ... ok
test result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_signals
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_systemd
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_term
running 1 test
test crates/radicle-term/src/table.rs - table (line 4) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests radicle_windows
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
error: 1 target failed:
`-p radicle-cli --test commands`
Exit code: 101
{
"response": "finished",
"result": "failure"
}