Commit Graph

35 Commits

Author SHA1 Message Date
Defelo 0ec084fc23 remote-helper: Support push --force-with-lease
Previously, the remote helper would not support the `--force-with-lease` option.
This change introduces this support and ensures that it works as intended.

Co-authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
2025-11-25 10:48:30 +00:00
Yaroslav Halchenko 32d77641ab chore: Fix spelling errors with codespell 2025-11-01 12:11:02 +01:00
Lorenz Leutgeb b7cfcfff75
workspace: A Little Less `git2`
Make `git2` an *optional* dependency of `radicle-cob`, and refactor
`radicle` to depend on crates that in turn do not depend on `git2`
*non-optionally*

The main offending dependency of `radicle-cob` is `radicle-git-ext`
from the `radicle-git` workspace in repository
(rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt) which *non-optionally* depends on
`git2`.

So, to achieve removal of this dependency:

 1. The crate is refactored to depend on the new crates
    `radicle-git-ref-format` `radicle-git-metadata`, and
    `radicle-oid` introduced in the previous commits, instead of
    `radicle-git-ext`.
 2. Some code from the `radicle-git-ext` crate in the `radicle-git`
    workspace in repository (rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt) is
    copied. See `crates/radicle-cob/src/backend/git/commit.rs`.

This cascades to `radicle` and its dependents.

  Firstly, the there is an
`impl Deref<Target=git2::Oid> for radicle_git_ext::Oid`. This made
it very convenient to just deref to obtain the wrapped `git2::Oid`,
so there are many expressions of the shape `*oid` in `radicle` and
its dependents. However `radicle-oid` does not provide
`impl Deref<Target=git2::Oid> for radicle_oid::Oid`, as notably,
`Target` is an associated type, and not a type parameter, so an
implementation of `Deref` would tie the new `Oid` too tightly to a
particular implementation.
Instead, work with `impl From<radicle_oid::Oid> for git2::Oid`
(which can be enabled using the feature flag `radicle-oid/git2`).
This explains the changes from `*oid` to `oid.into()` at every
transition to "`git2` land".

  Secondly, `radicle` and its dependents are refactored to also depend
on `radicle-git-ref-format` and (much less prominently)
`radicle-git-metadata` instead of `radicle-git-ext`
This is to avoid pulling in `git2` via these dependencies.

  Thirdly, as the re-exports in `crates/radicle/src/git.rs` change,
they are at the same time also cleaned up. Notably, the types from
`radicle-git-ref-format` are re-exported under `fmt` only, not "twice".

While overall this obviously is very much a breaking change, these
changes should mostly amount to changing from `Deref` to `Into`, i.e.,
`*oid` → `oid.into()` and rewriting imports for `radicle::git::fmt`.
This is indicated by the mostly mechanical nature of the changes to
`crates/radicle-{cli,node,remote-helper}`.
2025-10-13 16:14:23 +02:00
Lorenz Leutgeb 013da67aa1 cli/test: Skip `rad_diff` on macOS
macOS's `sed` requires an argument for `-i`, which we don't provide
in the example. Providing it makes the test fail on Linux.
Since this command is deprecated anyway, we just skip macOS.
2025-10-13 10:27:02 +01:00
Lorenz Leutgeb 7d6d2e51d8 cli/test: Clean up testing environment
This is a small cleanup to path generation during testing.
 1. Separate `rad_home` and `unix_home` to avoid confusion.
 2. Remove now unnecessary `trait HasHome`.
 3. Move setting `$USER` to the environment.
 4. Make paths shorter overall, to fix macOS tests.
2025-10-13 08:56:23 +01:00
Lorenz Leutgeb 7effa7c64c
node: Report sync status for given namespaces
The "seeds" command does only take a repository ID, and
carries the implicit assumption that the namespace with the same public
key as the Node ID of the receiving `radicle-node` process should be
used as reference for determining whether other nodes are in sync,
but no other namespace.

In the spirit of separating user and node identity, relax this, so that
the command also carries the public keys relative to which the sync
status should be computed. This allows to inspect sync status for
arbitrary namespaces via command.

For now, this feature is not exposed to the CLI, but `rad sync status`
always passes the public key of the active profile.
2025-10-09 07:55:11 +02:00
Lorenz Leutgeb 9689de9af0
node: Allow announcing refs for given public keys
The "announce references" command does only take a repository ID, and
carries the implicit assumption that the namespace with the same public
key as the Node ID of the receiving `radicle-node` process should be
announced, but no other namespace.

In the spirit of separating user and node identity, relax this, so that
the command also carries the public keys for which the announcement
should be made. This allows to announce arbitrary namespaces out of
storage via command.

For now, this feature is not exposed to the CLI, but rather:
 - `rad sync` always announce for the public key of the active profile.
 - `git-remote-rad` ditto, as it calls the same codepath.
2025-10-09 07:55:10 +02:00
Lorenz Leutgeb 2149770a4b
storage: Rewrite temporary repositories for clones
On Windows, all attempts to clone repositories failed with

    Fetch failed for rad:… from z6Mk…: Access is denied. (os error 5)

The reason is that, other than Unix-like systems, it forbids that
directories that are in use are moved.

To improve the situation, take back control over what is moved and
removed exactly by implementing `cleanup` instead of relying
on `impl Drop for tempfile::TempDir`.

A new type `TempRepository` is introduced to capture a repository that can be
used temporarily, and either cleaned up on failure, or moved on success.
This `TempRepository` can be constructed using `Storage::temporary_repository` –
renamed from `Storage::lock_repository` since it is only creating a temporary
resource and not locking it.

In the future (once Rust 1.89 is a little less cutting edge and more
widely available), we may opt for actual locking via
`std::fs::File::lock`.
2025-10-06 11:52:42 +02:00
Christopher Fredén c7bff28452 cli/issue: Use clap
A rewrite of the argument parsing portions of the `rad issue` subcommand
using `clap` instead of `lexopt`.

From a user's perspective, the look-and-feel of `rad issue` does not
change much. Although, the most prominent change is to rely on
`clap`'a default error template which looks a bit different from what we
use usually (`error: ...` vs. `✗ Error: ...`).

Leaving the above restriction aside, subcommand mis-usage is
reported more verbosely.

Also configure `clap` to use colored output for errors and help pages.
In help output, headers are colored with `Magenta` to match the overall
CLI styling.

Specify a type that captures the different actions that can be take
when using the `comment` command: `Comment`, `Reply`, and `Edit`.
Move the functions that run the corresponding actions into a `comment`
module.

To preserve the "default command" behaviour being `rad issue list`,
e.g., `rad issue --solved` behaving like `rad issue list --solved`,
introduce a new type `EmptyArgs`to also parse arguments without a
subcommand. In case no subcommand was parsed, construct `Command::List`
with from `EmptyArgs`.

Co-authored-by: Matthias Beyer <mail@beyermatthias.de>
Co-authored-by: Lorenz Leutgeb <lorenz.leutgeb@radicle.xyz>
Co-authored-by: Erik Kundt <erik@zirkular.io>
Co-authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
2025-09-28 18:02:55 +02:00
Lorenz Leutgeb 5a958b5ebd
cli/test: Output of `rad --help`
With migration subcommands to `clap` planned one-by-one,
prepare this to catch regressions.
2025-09-28 18:02:19 +02:00
Lorenz Leutgeb 9e1d6b1feb radicle: Detect current repository using `jj`
When using Jujutsu and a non-colocated Git repository, the detection
using `git2` directly fails (just as `git` in a shell would fail)
because there is no `.git` directory found by traversing up the
filesystem hierarchy.

Add an invocation of `jj git root` as a fallback.
2025-09-26 12:03:01 +01:00
Lorenz Leutgeb abc963f2f2 radicle-cli/tests: Add `rad_jj_patch` 2025-09-26 12:03:01 +01:00
Lorenz Leutgeb fd5043d572 radicle-cli-test: Prepare testing with `jj`
Most of this is taken from Jujutsu's own testing setup, see:
98d884827e/cli/tests/common/test_environment.rs (L106-L150)

Not all features are preserved, but this is good enough.
2025-09-26 12:03:01 +01:00
Lorenz Leutgeb 53522288c1 radicle-cli/tests: `fn program_reports_version`
Introduce this `fn` to check whether a program is available.
2025-09-26 12:03:01 +01:00
Lorenz Leutgeb fafb3493dc
cli: Detect key mismatch
When run with a secret and public key that do not cryptographically
match, `fn radicle_cli::terminal::io::signer` would prompt the user
for a password to unlock the secret key, and then carry on with a
mismatching key pair.

Fix this by verifying that the keys match in `MemorySigner::load`
and only swallow errors initializing the signer in cases where
prompting for a password makes sense. It does not make sense in the
case of mismatched keys.
2025-09-26 00:33:59 +02:00
Erik Kundt d2e10fdef6 cli/tests/commands: Clean up test `rad_patch`
This removes the `rad issue` test examples from being used in the
`fn rad_patch` test example execution. Removing it here is fine, since
it is being executed on its own anyways.
2025-09-16 19:40:35 +02:00
Lorenz Leutgeb 86ea33b0ca cli/clone: Add flag `--bare` 2025-09-15 10:21:09 +01:00
Lorenz Leutgeb e528c40a07 radicle-cli: Test initializing from bare repos 2025-09-15 10:21:09 +01:00
Lorenz Leutgeb 119a124897 radicle: the great Canonical rewrite
This change was inspired by the a story as old as time:

    𝕃𝕖𝕥 𝕦𝕤 𝕞𝕚𝕩 𝕠𝕦𝕣 𝕓𝕦𝕤𝕚𝕟𝕖𝕤𝕤 𝕝𝕠𝕘𝕚𝕔 𝕨𝕚𝕥𝕙 𝕠𝕦𝕣 𝕀𝕆!

It was motivated by the fact that the canonical quorum logic was spread across
two modules and also two different repositories (in the API sense). The
`radicle-remote-helper` contained special logic for computing the quorum, and
also relied on the logic with `radicle` itself. It would also mix using the
Radicle storage repository and the working copy repository – resulting in issues
where objects could exist in one and not the other.

The change begins by separating away the IO away from any of the business logic
in the `git::canonical` module. To follow along, there are two important submodules:

1. `voting` captures the different type of voting processes for commits and tags
  a. Tags are simple, where one `Oid` means one vote
  b. Commits are slightly more complicated. They begin with one `Oid` means one
     vote, but then it is expected that merge bases are calculated for pairs of
     commits. These merge bases are used to increase a vote for an `Oid` if it
     is the merge base of another commit.
2. `quorum` builds on top of `voting` and uses the voting processes as well as
    the `threshold` to find the quorum for the tag or commit reference.
  a. For tags, the first past the threshold wins, but if multiple pass then it
     is an error.
  b. For commits, the merge base process should be used to increase the votes,
     until the caller is ready to find the quorum. At this point, the commits
     that pass the `threshold` are then compared to find the commit that is the
     child-most commit of all other candidates. If they diverge, then an error
     is returned.

There is also a `convergence` module that captures the logic that is required
for the `radicle-remote-helper`. It essentially checks if a candidate object
matches the expected objects, tags or commits, and performs the necessary
convergence logic – checking that the candidate commit is converging with
at least of the other `Did`s.

These two quorum processes, and the convergence process, essentially act as
state machines and can be driven by the use of a Git repository for finding the
merge bases. This is where the `effects` module comes in. The `effects` capture
the necessary traits that are required to drive the state machines of the quorum
processes. It is expected that a Git repository implements these, and in fact, a
`git2::Repository` implementation is provided. The traits are useful, since it
means that a `git2::Repository` can be swapped out and the logic would stay the
same.

This all culminates into the new and improved `Canonical` and
`CanonicalWithConvergence`. Both of which have methods `find_quorum` for
performing the quorum process using a *single* provided repository.

This resulted in the semantic change of requiring that the
`radicle-remote-helper` pushes the candidate commit, irregardless of whether it
will be a fast-forward. For now, this is something that will be accepted while
the UX can be improved in the future by providing detailed warnings of the
divergence and ways to fix it. The benefit is that the tooling will never stop
someone from diverging if that is in fact what they want to do.
2025-08-25 18:57:50 +02:00
Sebastian Martinez 1d7478cd90 radicle: introduce `cob::common::Title`
This patch adds the `cob::common::Title` struct, this allows instead of
using `std::str::String` or generics and traits for it, to define more
precisely what a title should be.
It trims the provided string and makes sure it contains no carriage
return or new line characters.

Where a `std::str::String` makes more sense so it's easier to mutate it,
we keep the code as is.

Co-authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
2025-08-19 17:05:33 +02:00
Fintan Halpenny 2a0f6fd3c5 cli: extend `rad cob log` behaviour
Use the COB stream behaviour for implementing `rad cob log` and introducing the
from and until options.
2025-08-19 15:01:26 +01:00
Lorenz Leutgeb 9a7c22536b radicle/config/node: Use newtypes 2025-08-19 13:16:19 +02:00
Lorenz Leutgeb 0200e84aea bootstrap: Add IPv6, IPv4, Onion addresses
This should allow bootstrapping when DNS is not available or a
connection via Tor is desired.
2025-08-18 17:39:20 +02:00
Lorenz Leutgeb d7b48b9e29 radicle: Move logging setup calls to binaries 2025-08-07 08:30:59 +01:00
Lorenz Leutgeb 3d352f23e6 canonical: Support Annotated Tags
The logic for computing canonical references conflated the semantics of
annotated and lighweight tags, yielding confusing/wrong results. The
main culprit was the call to `peel_to_commit` in
`impl ReadRepository for Repository`, peeling tag objects away.

To resolve this, we separate out quroum computation per object type: one
implementation for commits, and one for tags.

Also move  move canonical error types and methods into their own
module to have a cleaner file structure for the main logic.

Capture the `BTreeMap<Oid, u8>` type into a `Votes` struct, with its own API.

We were getting the objects from the repository twice – once in `Canonical::new`
and once again in `ensure_commit_or_tag` – so the latter was removed.

Use an `enum CanonicalObject` to specify which object types the canonical
process supports.

During `converges`, separate commits and tags, and ensure that we are only
looking at objects of one type. Note, that Fintan think this is actually the
*wrong* place to do it because we already filter the objects – so we should
keep track of that information higher up the callstack.

Co-authored-by: Fintan Halpenny <fintan.halpenny@gmail.com>
2025-08-04 17:00:54 +02:00
Lorenz Leutgeb 9068033789 chore: Automated fixes generated by clippy 2025-07-25 11:25:05 +02:00
Sekhat Temporus 37ea81766e cli: improve default branch pick
When using `rad init` the default branch for the project can be predetermined by
inspecting the working copy repository.

To improve the experience, it will first check `init.defaultBranch` in the Git
configuration file.

If it does not find this, it falls back to choosing the default head of the
repository at the time. This may be HEAD, and in this case it will error and
provide helpful information.

The repository may also not have any default head at the time, and this also
results in an error and hint.
2025-07-23 07:43:27 +01:00
Fintan Halpenny 1fa30e2e88 cli: test missing commits for canonical quorum
Test the scenario where 2 delegates are collaborating, where one delegate is
missing the other's commit while pushing to the default branch.
2025-07-19 12:32:45 +01:00
Lorenz Leutgeb 1c20f64a26 node, protocol: Refactor
Mostly changes of `use`s and moving components to `radicle-protocol`,
with the goal of just getting `radicle-node` to work on top of the new
`radicle-protocol` crate.
2025-07-16 21:15:52 +02:00
Fintan Halpenny 8201225166 cli: test canonical tags
Add a test to exercise adding canonical reference rules and pushing new
canonical tags.

A change in `rad id` was required to allow adding a new payload to the document,
i.e. one that does not already exist.

Adding rules is also documented in the man page for `rad id`.
2025-07-16 16:54:43 +02:00
Lorenz Leutgeb 5a840983a8 node, cli: Refactor test environment 2025-07-09 11:47:50 +01:00
Johannes Kühlewindt 895ca5d025 cli: rad node connect using only NodeId
Allow the `rad node connect` command to use only a `NodeId` when connecting.

The `NodeId` is used to connect to known addresses, and will stop at the first
address that is connected to.
2025-07-03 12:13:49 +01:00
Lorenz Leutgeb af35e6f4d0 radicle-cli: Warn when using old names of nodes 2025-06-18 16:21:21 +02:00
Lorenz Leutgeb a354686bbc chore: Remove `seed.radicle.xyz`
The team node is not really special, remove occurrences.
2025-06-18 14:45:54 +02:00
Lorenz Leutgeb 5bc2dc677b repo: Move workspace crates into `crates` subdirectory
This keeps the top level of the repo cleaner, e.g.
<https://github.com/radicle-dev/heartwood> scrolls less and
<https://app.radicle.xyz/nodes/rosa.radicle.xyz/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5/>
might show more files at the top level.

This way we also don't have to explicitly name all crates in
`Cargo.toml`.
2025-06-09 15:09:21 +02:00