Commit Graph

3047 Commits

Author SHA1 Message Date
Josh Soref 8391599f61 oid: Fix spelling
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
2026-04-30 15:48:33 -04:00
Fintan Halpenny b482845e71 sigrefs/git/properties: Restore property tests
These tests fell under the radar when refactoring.
Restore them to the latest code so that these properties are tested.
2026-04-29 14:04:54 +02:00
Fintan Halpenny 92347a307f justfile: Add Rust tests to pre-push
The tests should be part of the pre-push step.
Introduce `test-rust`, as part of a new `test` group, and `pre-push` group.
2026-04-29 13:54:31 +02:00
Fintan Halpenny b4707e5502 cli: Amend jj tests
Update the output of `rad_jj_colocated_patch` so that it passes with the current version of `jj` being used:
```
jj --version
jj 0.35.0
```

Mark `rad_jj_bare` as `ignore` since it currently cannot determine the
RID from the `rad` remote.
2026-04-29 13:51:11 +02:00
Lorenz Leutgeb 378365aa42 term: Deny `print_stdout`
To prevent regressions, `#![deny(clippy::print_stdout)]` is added to
`radicle-term`, similar to `radicle-cli`.
2026-04-29 10:57:24 +02:00
Lorenz Leutgeb cc70d7dad4 radicle/cob: Do not return `BrokenPipe` from external COB invocation
A handle for standard input can always be expected here.
2026-04-29 10:57:24 +02:00
Lorenz Leutgeb 553a3ebd0a cli/cob: Remove check of `BrokenPipe`
The swallowing of `BrokenPipe` is processed further down the stack,
so this case is no longer needed.
2026-04-29 10:57:24 +02:00
Fintan Halpenny 56625a42f7 term: Rename print_inline to print
The name `print_inline` was used because, previously, the `print`
function name was taken.

This has now changed, allowing the rename of this function to happen.
2026-04-29 10:57:24 +02:00
Fintan Halpenny 2262b8d9bd term: Rename print to println
The name `print` was used because, previously, the `println`
function name was taken.

This has now changed, allowing the rename of this function to happen.
2026-04-29 10:57:24 +02:00
Fintan Halpenny e7d519cf65 term: Rename println to println_prefixed
This function was mistakenly called `println`, despite it also wanting
a prefix.

Rename the function so that it better reflects its behaviour.
2026-04-29 10:57:24 +02:00
Fintan Halpenny 1a31a9f541 cli: Handle broken pipe (SIGPIPE) gracefully
Prevent `rad` from panicking when its stdout is a closed pipe, e.g.
when running `rad config | head`.

The broken pipes are handled at the output boundaries using three
layers of defence:

1. Inner: Route all stdout output through `term::print()` and
   `term::print_inline()`, which use explicit `writeln!`/`write!` to
   a locked stdout and silently ignore write errors. All `println!`
   calls in `radicle-cli` and `radicle-term` are converted.
2. Middle: Catch `BrokenPipe` errors that propagate up via `anyhow`
   from subcommands in `main::run()`, and exit cleanly with code 0.
3. Outer: Install a panic hook that intercepts `println!` panics
   containing "Broken pipe" (from dependencies like clap) and exits
   cleanly, chained in front of human_panic's hook.

To prevent regressions, `#![deny(clippy::print_stdout)]` is added to
`radicle-cli`, requiring all future stdout output to go through the
safe `term::` functions.

Note: `eprintln!` calls (stderr) are not handled, as broken stderr
pipes are extremely rare in practice.
2026-04-29 10:57:24 +02:00
Fintan Halpenny e1f16bee26 term: Catch EPIPE and swallow
Rust (since 1.62) ignores EPIPE by default (see [Rust #62569]),
causing writes to closed pipes to return `io::ErrorKind::BrokenPipe`
errors.  The `println!` macro panics on these errors, producing a
confusing backtrace instead of the silent exit expected of Unix CLI
tools.

To avoid the use of `print!` and `println!`, the `radicle-term`
helper functions use `write!` and `writeln!`. This lays the groundwork
for the `rad` CLI to transition to using only `radicle-term`
functions.

There was a first attempt made, which is documented here.  A
process-wide SIGPIPE reset (`SIG_DFL`) was ruled out because `rad`
manages child processes via libgit2 pipes and communicates with the
radicle node over Unix sockets. Resetting SIGPIPE globally caused
`rad` itself to be killed during internal pipe operations (e.g. during
`rad remote add --fetch`), producing flaky test failures.

[Rust #62569] https://github.com/rust-lang/rust/issues/62569
2026-04-29 10:57:24 +02:00
Fintan Halpenny 8f4b90db7b cli/test: Add broken pipe (SIGPIPE) tests
Add tests that verify the `rad` binary does not panic when its stdout
is a broken pipe (e.g., `rad config | head -1`).

Rust (since 1.62) ignores SIGPIPE by default (see [Rust #62569]),
causing `println!` to panic with "failed printing to stdout: Broken
pipe" instead of exiting silently. This is the standard Unix behaviour
expected of CLI tools.

These tests currently fail on `rad config` and `rad self`, and are set to `ignore`, to be enabled when fixed.

[Rust #62569]: https://github.com/rust-lang/rust/issues/62569
2026-04-29 10:57:24 +02:00
Lorenz Leutgeb 7b07e57b9c treewide/test: Use `impl Arbitrary for Oid`
Many usages of `Oid::from_sha1` are actually in the context of
generating arbitrary `Oid`s. In these cases, `impl Arbitrary for Oid`
may be used directly.
2026-04-28 14:35:38 +02:00
Lorenz Leutgeb 88a7252e33 oid: Fix `impl Arbitrary for Oid`
`fill` would cause the OID to consist of 20 repetitions of the same
byte every time, not an arbitrary OID.
2026-04-28 14:35:38 +02:00
Lorenz Leutgeb deabe6d2ce oid: Nicer panic message
The panic message that `unimplemented!` generates is more appropriate.
2026-04-28 14:35:38 +02:00
Lorenz Leutgeb 60f42bff53 protocol: Minimize scope of `radicle::git::raw`
A tiny refactoring to make it less inviting to use
`radicle::git::raw`.
2026-04-28 14:35:38 +02:00
Lorenz Leutgeb 8dba3ca9de protocol: Decode `radicle_oid::Oid` without `git2`
`impl Decode for Oid` uses `git2` via `radicle::git::raw`.
This is unnecessarily complex.
2026-04-28 14:35:38 +02:00
Lorenz Leutgeb 3d1b37fcbb oid: Make `Oid::SHA1_LEN` public
The length of a SHA-1 object identifier is defined in various locations.
Instead of this redundancy, make the definition in `radicle-oid` public, and
use it in `radicle{-{core,protocol},}`.
2026-04-28 14:35:38 +02:00
Lorenz Leutgeb f46624b81c oid: SHA1_DIGEST_LEN → Oid::LEN_SHA1
A small refactor to move this constant closer to the `enum` that it is
about. An associated constant constant fits more nicely.

Reason for changing the name from `SHA1_*_LEN` to `LEN_SHA1`: As we
think about the object format in the future becoming variable, we
would have that the length of an OID is parameterized by the object
format. So, we imagine a function `fn len(format: ObjectFormat)`, to
be called as `Oid::len(ObjectFormat::Sha1)`. This is the new order
we use.
2026-04-28 14:35:38 +02:00
Lorenz Leutgeb 4c605c0671 radicle: Avoid comparison with `ZERO_SHA1`
When the underlying hash function is not important, but only the fact
whether the OID is the zero sentinel, use `Oid::is_zero`. This is more
flexible, as it is forward compatible with the SHA-256 object format.
2026-04-28 14:35:38 +02:00
Lorenz Leutgeb ecca50a5f9 treewide: Avoid `git2::Oid::zero`
In multiple instances, `git2::Oid::zero` is used to obtain an OID that
is equivalent to `radicle_oid::Oid::SHA1_ZERO`. This is needlessly
complex.
2026-04-28 14:35:38 +02:00
Lorenz Leutgeb f65175397f oid: `const ZERO_SHA1` instead of `fn sha1_zero`
`fn sha1_zero` can be `const fn`, and since it does not take arguments,
it can just be a `const`. There is no good reason to not have a constant.

Justification for changing the order to first specify "zero" and then
"SHA1": In the future we will support multiple object formats. A
function that produces a zero OID would be parameterized by the object
format then. Imagine `fn zero(format: ObjectFormat)` which would be
called as `Oid::zero(ObjectForma::Sha1)`. In anticipation, we change the
ordering to match.
2026-04-28 14:35:38 +02:00
Lorenz Leutgeb aa177b0400
treewide: radicle.{xyz → dev,network}
We have moved to radicle.dev, so adjust the domain name in various
places.

Not changed:
 - COB type names
 - systemd secret identifiers
2026-04-27 18:34:30 +02:00
Lorenz Leutgeb cb57e6560f
node: Rename systemd Credentials
Because we changed our main domain from radicle.xyz to radicle.dev, we
should also change our systemd credential identifiers.

Handling is move into a new function, which also checks the old names
for backwards compatibility.
2026-04-27 18:21:26 +02:00
Lorenz Leutgeb 06ff36ebc4
cli: Update warning for new bootstrap node names 2026-04-27 18:20:25 +02:00
Lorenz Leutgeb 22287fd94a
radicle: Change names of bootstrap nodes 2026-04-27 18:19:47 +02:00
Lorenz Leutgeb 3bc8abdc29
radicle/node/config: Use `IndexSet`
With `connect: std::collections::HashSet`, we suffer unpredictable reordering
of values in our test cases.

Use `connect: indexmap::IndexSet` instead, which preserves insertion
order for iteration.
2026-04-27 17:36:20 +02:00
Fintan Halpenny 099722dd08 radicle: Propagate `radicle-oid/qcheck`
The `radicle-oid/qcheck` feature was not added for `radicle`'s
`qcheck` feature.
Add this so that it propagates for use in downstream crates.
2026-04-24 08:29:34 +01:00
Adrian Duke 5d8988b1a2 CONTRIBUTING/HACKING: Add note on direnv
Add a note on the use of `direnv`, and the availability of the
`.envrc.sample` file.
2026-04-23 08:56:48 +01:00
Richard Levitte 8af0b623fe .envrc: Replace .envrc with .envrc.sample
The `.envrc` file acts as a personal setup file, and should not be checked in.

The team provides `.envrc.sample` where contributors can choose to:

    echo 'source_env .envrc.sample' >> .envrc

To bootstrap their own `.envrc` file.
2026-04-23 08:56:48 +01:00
Adrian Duke bdd991a5f1 e2e: Fix race condition in test_non_fastforward_identity_doc
Fixes an intermittent failure caused by a race condition with background
gossip messages.

Previously Eve's `RefsAnnounced` message could be processed by Bob
after the test updated the identity document to include Alice-Laptop
as a delegate. This caused Bob to unexpectedly fetch the issue in the
background, failing the `!has_issue` assertion.

We now explicitly wait for Bob to receive Eve's announcement before
updating the identity document.
2026-04-23 08:45:06 +01:00
Adrian Duke 802e4726e3 e2e: Replace the hardcoded 1-second sleep in `test_connection_crossing` with polling loop
Avoid relying on a thread sleep to wait for the nodes to connect.

Instead rely on the connection condition being established by using a polling loop.
The loop is controlled by a number of iterations, panicking if the
number of iterations is exceeded.
2026-04-23 08:42:32 +01:00
Adrian Duke bfb54bf4be e2e: Fix flakiness in `test_connection_crossing` test
Addresses intermittent failures in the `test_connection_crossing` e2e test,
which were particularly prevalent on slower CI environments (such as
`rust:trixie`).

Previously, the test spawned two threads to make Alice and Bob dial each
other concurrently and strictly asserted that the "preferred" peer (the
one with the higher Node ID) would always win the `Outbound` link direction.
However, this assumption is flawed in real-world, OS-level network execution
due to two race conditions:

1. Thread Scheduling: One thread could execute and fully establish a
   connection before the other thread even began processing its dial command.
2. Reactor Event Ordering: A node's reactor might wake up and process an
   incoming TCP connection from its peer *before* it processes the `Connect`
   command sent by the test. When it finally processes the `Connect` command,
   it sees a session already exists and skips dialing entirely.

In both scenarios, a true "simultaneous crossing" never occurs. Instead, a
standard sequential connection happens, meaning the link direction is dictated
by whoever dialed first, not by the "preferred" peer logic. This caused the
strict `left: Outbound, right: Inbound` assertions to panic.

To fix this, two changes were made:
- Introduced a `std::sync::Barrier` to synchronize the two test threads.
  This forces both threads to wait for each other before calling `.connect()`,
  maximizing the probability of a true simultaneous dial.
- Relaxed the final assertions. Because OS-level TCP handshakes and reactor
  polling can never guarantee perfect simultaneity, we no longer assert
  *which* peer gets the `Outbound` link. Instead, we assert the core invariant:
  that exactly one connection is established between the nodes, and that their
  link directions are opposite (`s1.link != s2.link`).
2026-04-23 08:39:03 +01:00
Lorenz Leutgeb bbb1279604 cli/config: Obsolete `{get,set,push,remove,unset}` 2026-04-22 15:45:57 +01:00
Lorenz Leutgeb f83eb57719 ci: Update GitHub action `actions/cache`
Version 4 uses Node.js 22, which GitHub warns about and suggests to
update.

Changelog is at <https://github.com/actions/cache#v5>.
2026-04-22 15:31:31 +01:00
Lorenz Leutgeb 224c03daf1 node: Expand `target_os` condition for keep-alive
`KeepAlive::with_retries` is not just unavailable on Windows, but on a
bunch of other operating systems. Copy the condition over from
`socket2`.
2026-04-22 12:06:12 +01:00
Lorenz Leutgeb 4a81673d0e node: Fix conditions for socket activation
The implementation of socket activation requires not only the `systemd`
feature, but also `socket2`.
2026-04-22 12:03:05 +01:00
Richard Levitte 354805aeb3 cli/test: Fix failures related to remote HEAD
Two tests assumed that `refs/remotes/rad/HEAD` would automatically be
crated.  It turns out, however, that this only the case if executed
with Git 2.48.0 or newer. Git versions older than 2.48.0 do not touch
'refs/remotes/<remote>/HEAD'.

Git 2.48.0 and newer will update `refs/remotes/<remote>/HEAD` by
default. This can be disabled by setting
`remote.<remote>.followRemoteHEAD` to "never". Thus, with these
versions, setting that configuration to "never" emulates the behaviour
of older versions.

Therefore, to ensure that test results are consistent across Git
versions before and after 2.48.0, the fixture of test repositories
now include setting 'remote.rad.followRemoteHEAD = never'.

The affected tests are adjusted accordingly.

This change can possibly be reverted once Radicle requires usage of Git
2.48.0 or newer.

Co-authored-by: Lorenz Leutgeb <lorenz.leutgeb@radicle.xyz>
2026-04-21 23:15:34 +02:00
Lorenz Leutgeb 6ce2d00be2 flake: Set `RUST_BACKTRACE` in development shell 2026-04-20 09:24:12 +01:00
Lorenz Leutgeb 2d20e249fd cli/init: Deduplicate `ScopeParser` 2026-04-20 09:21:00 +01:00
Richard Levitte f8ec40a33f just: Make it possible to give your own cargo command
Do it by setting the environment variable 'CARGO_CMD' when running
'just', or by overriding the justfile variable 'cargo_cmd', like so:

    CARGO_CMD='cargo --locked' just ...

or:

    just cargo_cmd='cargo --locked' ...
2026-04-20 09:11:07 +01:00
Lorenz Leutgeb 4f647b2a10 Spell "Radicle" with a captial R
In documentation and output, spell the name of the project with a
captial "R".
2026-04-17 17:15:31 +01:00
Lorenz Leutgeb bb97414a2d cli: Deduplicate RID Argument Handling
Multiple commands take an RID as a command line argument. However, the
handling, which usually involves falling back to detection of the
repository associated with the current directory, is duplicated many
times over.

Introduce `fn rid_or_cwd` which can be used by many commands.
2026-04-17 17:09:15 +01:00
Lorenz Leutgeb f2e96c96ce cli: Rename all `RepoId` args to `repo`
Many commands take RIDs as arguments. Most of them call the
corresponding struct member `repo`, some call it `rid`.

For consistency, name these members `repo` for all commands.

This change is intended to be invisible to the user. That is why,
in some cases, the "long" version of the argument must be renamed to
match `--rid`.
2026-04-17 17:09:15 +01:00
Fintan Halpenny 07c624499c just: Check for Git and Jujutsu-VCS conflict markers 2026-04-17 14:36:58 +01:00
Fintan Halpenny f00a1624df codespell: Add .direnv to skip list 2026-04-17 14:36:58 +01:00
Fintan Halpenny 8a38fa8c73 typos: Rule for ... -> …
Extend the typos rules to include `"..." = "…"`.
2026-04-17 14:36:58 +01:00
Fintan Halpenny fe353ea7e9 codespell: Rule for ... -> …
Creates a new .codespell-dictionary.txt to extend the default set of rules.

The first rule is `...|…`.
2026-04-17 14:36:58 +01:00
Adrian Duke 179a080867 just: Introduce commit-msg hook for typos
Add a `commit-msg` hook to continuously run `typos` against the COMMIT_MSG.
2026-04-17 14:36:58 +01:00