mirror of
https://seed.radicle.dev/rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5.git
synced 2026-10-22 21:00:31 +08:00
Create patch
Rad-Related:08844cf184Rad-Related:e130b4dc06Rad-Resource:0f44f78b78
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
[advisories]
|
||||
ignore = [
|
||||
"RUSTSEC-2023-0071", # issue bd8af7a5e3fd02c0963d7550a5cc2cab00950775
|
||||
]
|
||||
@@ -1,4 +0,0 @@
|
||||
# We want the tests to stop running if they are running
|
||||
# longer than 1min
|
||||
[profile.default]
|
||||
slow-timeout = { period = "30s", terminate-after = 2, grace-period = "10s" }
|
||||
@@ -1,4 +0,0 @@
|
||||
target
|
||||
scripts
|
||||
debian
|
||||
systemd
|
||||
@@ -1,3 +0,0 @@
|
||||
/target
|
||||
/radicle-cli/target
|
||||
/build/artifacts
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
cloudhead ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIL460KIEccS4881p7PPpiiQBsxF+H5tgC6De6crw9rbU
|
||||
adaszko ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIM7gBkM8PwWFe5tiJkCXdYvrYSKapm8MSNVzjJWBy7DL
|
||||
fintohaps ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOl44zVBYvu1Uhmq9nshhic4r1Moz5fdNOoWTtdJrUwd
|
||||
erikli ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBrJyJTwj/xG7F7qY0HDFXbb8A+xNNH8eILQ8hlvKW7/
|
||||
slack-coder ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDJhzdajJ/SzvIlpywXRiXU5K3oxNjCoDawwIiSAJrbU
|
||||
sebastinez ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFw+wCPZUh8OndhgavJMSZluorVvzJjz22PH81XrkvIu
|
||||
zlatan ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPE9CnXiiTVCbXGQJSgZuboN4aPzNw4klqp/6/+djcD5
|
||||
rudolfs ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIPueml1FxzjvwbD7vRZfwoaoyuxLy0L+WLBwSNiVoJe5
|
||||
dave ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEyzfcjfYYBnTLWEZYjsHUW1Pczg6HN0BHMWmoV5dg96
|
||||
canndrew ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICGRGjNA2OMOCbrEKIIO7cUvArmryX18XQB2AH+czSTb
|
||||
yorgos ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAILc7h6NDgUUORlg/M9n9jq5EK5o76PKtyWBDEAc/ogrs
|
||||
@@ -1,9 +0,0 @@
|
||||
shell: |
|
||||
cargo --version
|
||||
rustc --version
|
||||
|
||||
cargo fmt --check
|
||||
cargo clippy --all-targets --workspace -- --deny clippy::all
|
||||
cargo build --all-targets --workspace
|
||||
cargo doc --workspace
|
||||
cargo test --workspace --no-fail-fast
|
||||
@@ -0,0 +1 @@
|
||||
{"target":"delegates","title":"signals: Use `signals_receipts` crate instead","type":"edit"}
|
||||
@@ -1,3 +0,0 @@
|
||||
# ARCHITECTURE
|
||||
|
||||
See the [Protocol Guide](https://radicle.xyz/guides/protocol).
|
||||
-236
@@ -1,236 +0,0 @@
|
||||
# CONTRIBUTING
|
||||
|
||||
Contributions are very welcome. When contributing code, please follow these
|
||||
simple guidelines.
|
||||
|
||||
* Follow the coding guidelines when proposing code changes ([see below](#code-style)).
|
||||
* Write properly formatted git commits messages ([see below](#writing-git-commit-messages)).
|
||||
* Read the DCO and if you are contributing a significant amount of code, make sure your commits are signed off, using `git commit -s`.
|
||||
* Contribute code via a Radicle Patch, by following the [guide][].
|
||||
* If it's a feature addition or major change, check with the maintainers first
|
||||
before submitting a patch. We wouldn't want you to waste your time!
|
||||
* If you need help or would like to discuss your changes, come to our community chat on [Zulip][zulip].
|
||||
|
||||
[guide]: https://radicle.xyz/guides/user#working-with-patches
|
||||
[zulip]: https://radicle.zulipchat.com
|
||||
|
||||
## Submitting patches
|
||||
|
||||
Patch formatting follows the same rules as commit formatting. See below.
|
||||
|
||||
## Linting & formatting
|
||||
|
||||
Always check your code with the linter (`clippy`), by running:
|
||||
|
||||
$ cargo clippy --workspace --tests
|
||||
|
||||
And make sure your code is formatted with, using:
|
||||
|
||||
$ cargo fmt
|
||||
|
||||
Finally, ensure there is no trailing whitespace anywhere.
|
||||
|
||||
## Running tests
|
||||
|
||||
Make sure all tests are passing with:
|
||||
|
||||
$ cargo test --workspace
|
||||
|
||||
## Checking the docs
|
||||
|
||||
If you make documentation changes, you may want to check whether there are any
|
||||
warnings or errors:
|
||||
|
||||
$ cargo doc --workspace --all-features
|
||||
|
||||
## Code style
|
||||
|
||||
The following code guidelines will help make code review smoother.
|
||||
|
||||
### Use of `unwrap` and `expect`
|
||||
|
||||
Use `unwrap` only in either of three circumstances:
|
||||
|
||||
1. Based on manual static analysis, you've concluded that it's impossible for
|
||||
the code to panic; so unwrapping is *safe*. An example would be:
|
||||
|
||||
let list = vec![a, b, c];
|
||||
let first = list.first().unwrap();
|
||||
|
||||
2. The panic caused by `unwrap` would indicate a bug in the software, and it
|
||||
would be impossible to continue in that case.
|
||||
|
||||
3. The `unwrap` is part of test code, ie. `cfg!(test)` is `true`.
|
||||
|
||||
In the first and second case, document `unwrap` call sites with a comment prefixed
|
||||
with `SAFETY:` that explains why it's safe to unwrap, eg.
|
||||
|
||||
// SAFETY: Node IDs are valid ref strings.
|
||||
let r = RefString::try_from(node.to_string()).unwrap();
|
||||
|
||||
Use `expect` only if the function expects certain invariants that were not met,
|
||||
either due to bad inputs, or a problem with the environment; and include the
|
||||
expectation in the message. For example:
|
||||
|
||||
logger::init(log::Level::Debug)
|
||||
.expect("logger must only be initialized once");
|
||||
|
||||
### Module imports
|
||||
|
||||
Modules are declared at the top of the file, before the imports. Public modules
|
||||
are separated from private modules with a blank line:
|
||||
|
||||
mod git;
|
||||
mod storage;
|
||||
|
||||
pub mod refs;
|
||||
|
||||
use std::time;
|
||||
use std::process;
|
||||
|
||||
...
|
||||
|
||||
Imports are organized in groups, from least specific to more specific:
|
||||
|
||||
use std::collections::HashMap; // First, `std` imports.
|
||||
use std::process;
|
||||
use std::time;
|
||||
|
||||
use git_ref_format as format; // Then, external dependencies.
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::crypto::PublicKey; // Finally, local crate imports.
|
||||
use crate::storage::refs::Refs;
|
||||
use crate::storage::RemoteId;
|
||||
|
||||
### Variable naming
|
||||
|
||||
Use short 1-letter names when the variable scope is only a few lines, or the context is
|
||||
obvious, eg.
|
||||
|
||||
if let Some(e) = result.err() {
|
||||
...
|
||||
}
|
||||
|
||||
Use 1-word names for function parameters or variables that have larger scopes:
|
||||
|
||||
pub fn commit(repo: &Repository, sig: &Signature) -> Result<Commit, Error> {
|
||||
...
|
||||
}
|
||||
|
||||
Use the most descriptive names for globals:
|
||||
|
||||
pub const KEEP_ALIVE_DELTA: LocalDuration = LocalDuration::from_secs(30);
|
||||
|
||||
### Function naming
|
||||
|
||||
Stay concise. Use the function doc comment to describe
|
||||
what the function does, not the name. Keep in mind functions are in the
|
||||
context of the parent module and/or object and repeating that would be
|
||||
redundant.
|
||||
|
||||
### Logging
|
||||
|
||||
When writing log statements, always include a `target` and include enough
|
||||
context in the log message that it is useful on its own, eg.
|
||||
|
||||
debug!(target: "service", "Routing table updated for {rid} with seed {nid}");
|
||||
|
||||
Check the file you are working on for what the `target` name should be; most
|
||||
logs should be at the *debug* level.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Before adding any code dependencies, check with the maintainers if this is okay.
|
||||
In general, we try not to add external dependencies unless it's necessary.
|
||||
Dependencies increase counter-party risk, build-time, attack surface, and
|
||||
make code harder to audit.
|
||||
|
||||
## Documentation
|
||||
|
||||
Public types and functions should be documented. Modules *may* be documented,
|
||||
if you see the need.
|
||||
|
||||
Code comments should usually be full english sentences, and add missing context
|
||||
for the reader:
|
||||
|
||||
// Ensure that our inventory is recorded in our routing table, and we are tracking
|
||||
// all of it. It can happen that inventory is not properly tracked if for eg. the
|
||||
// user creates a new repository while the node is stopped.
|
||||
for rid in self.storage.inventory()? {
|
||||
...
|
||||
|
||||
## Proposing changes
|
||||
|
||||
When proposing changes via a patch:
|
||||
|
||||
* Isolate changes in separate commits to make the review process easier.
|
||||
* Don't make unrelated changes, unless it happens to be an obvious improvement to
|
||||
code you are touching anyway ("boyscout rule").
|
||||
* Rebase on `master` when needed.
|
||||
* Keep your changesets small, specific and uncontroversial, so that they can be
|
||||
merged more quickly.
|
||||
* If the change is substantial or requires re-architecting certain parts of the
|
||||
codebase, write a proposal in English first, and get consensus on that before
|
||||
proposing the code changes.
|
||||
|
||||
**Preparing commits**
|
||||
|
||||
1. Each commit in your patch must pass all the tests, lints and checks. This is
|
||||
so that they can be built into binaries and to make git bisecting possible.
|
||||
2. Do not include any commits that are fixes or refactorings of previous patch
|
||||
commits. These should be squashed to the minimal diff required to make the
|
||||
change, unless it's helpful to make a large change over multiple commits,
|
||||
while still respecting (1). Do not include `fixup!` commits either.
|
||||
3. A commit *may* include a category prefix such as `cli:` or `node:` if it
|
||||
mainly concerns a certain area of the codebase. For example. These prefixes
|
||||
should usually be the name of the crate, minus any common prefix. Eg.
|
||||
`cli:`, and *not* `radicle-cli:`. For documentation, you can use `docs:`,
|
||||
and for CI-related files, you can use `ci:`.
|
||||
|
||||
To help with the above, use `git commit --amend` and `git rebase -i`. You can
|
||||
also interactively construct a commit from a working tree using `git add -p`.
|
||||
|
||||
## Writing commit messages
|
||||
|
||||
A properly formed git commit subject line should always be able to complete the
|
||||
following sentence:
|
||||
|
||||
If applied, this commit will _____
|
||||
|
||||
In addition, it should be capitalized and *must not* include a period.
|
||||
|
||||
For example, the following message is well formed:
|
||||
|
||||
Add support for .gif files
|
||||
|
||||
While these ones are **not**: `Adding support for .gif files`,
|
||||
`Added support for .gif files`, `add support for .gif files`.
|
||||
|
||||
When it comes to formatting, here's a model git commit message[1]:
|
||||
|
||||
Capitalized, short (50 chars or less) summary
|
||||
|
||||
More detailed explanatory text, if necessary. Wrap it to about 72
|
||||
characters or so. In some contexts, the first line is treated as the
|
||||
subject of an email and the rest of the text as the body. The blank
|
||||
line separating the summary from the body is critical (unless you omit
|
||||
the body entirely); tools like rebase can get confused if you run the
|
||||
two together.
|
||||
|
||||
Write your commit message in the imperative: "Fix bug" and not "Fixed bug"
|
||||
or "Fixes bug." This convention matches up with commit messages generated
|
||||
by commands like git merge and git revert.
|
||||
|
||||
Further paragraphs come after blank lines.
|
||||
|
||||
- Bullet points are okay, too.
|
||||
|
||||
- Typically a hyphen or asterisk is used for the bullet, followed by a
|
||||
single space, with blank lines in between, but conventions vary here.
|
||||
|
||||
- Use a hanging indent.
|
||||
|
||||
---
|
||||
|
||||
[1]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
|
||||
Generated
-3273
File diff suppressed because it is too large
Load Diff
-41
@@ -1,41 +0,0 @@
|
||||
[workspace]
|
||||
package.version = "0.9.0"
|
||||
members = [
|
||||
"radicle",
|
||||
"radicle-cob",
|
||||
"radicle-cli",
|
||||
"radicle-cli-test",
|
||||
"radicle-crdt",
|
||||
"radicle-crypto",
|
||||
"radicle-dag",
|
||||
"radicle-fetch",
|
||||
"radicle-node",
|
||||
"radicle-remote-helper",
|
||||
"radicle-ssh",
|
||||
"radicle-tools",
|
||||
"radicle-signals",
|
||||
"radicle-systemd",
|
||||
]
|
||||
default-members = [
|
||||
"radicle",
|
||||
"radicle-cli",
|
||||
"radicle-cob",
|
||||
"radicle-crdt",
|
||||
"radicle-crypto",
|
||||
"radicle-node",
|
||||
"radicle-ssh",
|
||||
"radicle-remote-helper",
|
||||
"radicle-term",
|
||||
"radicle-signals",
|
||||
"radicle-systemd",
|
||||
]
|
||||
resolver = "2"
|
||||
|
||||
[profile.container]
|
||||
inherits = "release"
|
||||
debug = true
|
||||
incremental = false
|
||||
|
||||
[workspace.lints]
|
||||
clippy.type_complexity = "allow"
|
||||
clippy.enum_variant_names = "allow"
|
||||
@@ -1,28 +0,0 @@
|
||||
Developer's Certificate of Origin 1.1
|
||||
Copyright © 2004, 2006 The Linux Foundation and its contributors.
|
||||
|
||||
---
|
||||
|
||||
By making a contribution to this project, I certify that:
|
||||
|
||||
(a) The contribution was created in whole or in part by me and I
|
||||
have the right to submit it under the open source license
|
||||
indicated in the file; or
|
||||
|
||||
(b) The contribution is based upon previous work that, to the best
|
||||
of my knowledge, is covered under an appropriate open source
|
||||
license and I have the right under that license to submit that
|
||||
work with modifications, whether created in whole or in part
|
||||
by me, under the same open source license (unless I am
|
||||
permitted to submit under a different license), as indicated
|
||||
in the file; or
|
||||
|
||||
(c) The contribution was provided directly to me by some other
|
||||
person who certified (a), (b) or (c) and I have not modified
|
||||
it.
|
||||
|
||||
(d) I understand and agree that this project and the contribution
|
||||
are public and that a record of the contribution (including all
|
||||
personal information I submit with it, including my sign-off) is
|
||||
maintained indefinitely and may be redistributed consistent with
|
||||
this project or the open source license(s) involved.
|
||||
-178
@@ -1,178 +0,0 @@
|
||||
# HACKING
|
||||
|
||||
Welcome to the Radicle "Heartwood" hacking guide!
|
||||
|
||||
We appreciate your interest in contributing to the Radicle project. If you come across
|
||||
a bug or a missing feature, please feel free to submit a patch. This guide is meant as
|
||||
an introduction to the codebase, on how to debug issues, write tests and navigate the
|
||||
repository.
|
||||
|
||||
Please make sure to read [CONTRIBUTING.md](CONTRIBUTING.md) before submitting code,
|
||||
and follow the included guidelines. To download a development version of Radicle,
|
||||
see the [README.md](README.md).
|
||||
|
||||
For an architectural overview of Heartwood, see [ARCHITECTURE.md](ARCHITECTURE.md).
|
||||
|
||||
---
|
||||
|
||||
The repository is structured in *crates*, as follows:
|
||||
|
||||
* `radicle`: The Radicle standard library that contains shared libraries used across the project.
|
||||
* `radicle-cli`: the Radicle command-line interface (`rad`).
|
||||
* `radicle-cli-test`: The Radicle CLI testing framework, for writing documentation tests.
|
||||
* `radicle-cob`: Radicle Collaborative Objects (COBs). Provides a way of creating and traversing edit histories.
|
||||
* `radicle-crdt`: Conflict-free replicated datatypes (CRDTs) used for things like discussions and patches.
|
||||
* `radicle-crypto`: A wrapper around Ed25519 cryptographic signing primitives.
|
||||
* `radicle-dag`: A simple directed acyclic graph implementation used by `radicle-cob`.
|
||||
* `radicle-node`: The radicle peer-to-peer daemon that enables users to connect to the network and share code.
|
||||
* `radicle-remote-helper`: A Git remote helper for `rad://` remotes.
|
||||
* `radicle-ssh`: OpenSSH functionality, including a library used to interface with `ssh-agent`.
|
||||
* `radicle-term`: A generic terminal library used by the Radicle CLI.
|
||||
* `radicle-tools`: Tools used to aid in the development of Radicle.
|
||||
|
||||
## Running in debug mode
|
||||
|
||||
To run the services or the CLI in debug mode, use `cargo run -p <package>`.
|
||||
|
||||
For example, the equivalent of `rad auth` in debug mode would be:
|
||||
|
||||
$ cargo run -p radicle-cli --bin rad -- auth
|
||||
|
||||
Arguments after the `--` are passed directly to the `rad` executable.
|
||||
|
||||
This is useful if you are running multiple nodes on the same machine. You can also
|
||||
specify different listen addresses for the peer-to-peer protocol using `--listen`.
|
||||
To view all options, run `cargo run -p radicle-node -- --help`.
|
||||
|
||||
You may want to set the appropriate environment variables before running these commands
|
||||
to prevent them from interfering with an existing installation of radicle. See the
|
||||
following section on environment variables.
|
||||
|
||||
## Running in isolation
|
||||
|
||||
When testing things, if you want to run one or more nodes in isolation from the
|
||||
public Radicle network of nodes:
|
||||
|
||||
* In the node `config.json` file:
|
||||
- set `preferredSeeds` to `[]`
|
||||
- set `node.connect` to `[]`
|
||||
- set `node.network` to `test`
|
||||
- set `node.peers.type` to `static`
|
||||
|
||||
This allows you to make any changes to the repositories on your nodes without
|
||||
having them replicated to outside your nodes. This would be good to do for any
|
||||
experiments you make that might be burdensome for other nodes, such as creating
|
||||
very large repositories.
|
||||
|
||||
## Environment variables
|
||||
|
||||
When developing radicle, some environment variables may be used to make the
|
||||
development environment more friendly.
|
||||
|
||||
**`RAD_HOME`**
|
||||
|
||||
Set this to a path on your file system where you'd like radicle to store keys
|
||||
and repositories. Typically you'll want to set this to a temporary folder, eg.
|
||||
`/tmp/radicle`, that can be safely deleted. If set, all radicle data will be
|
||||
stored within this folder.
|
||||
|
||||
**`RAD_KEYGEN_SEED`**
|
||||
|
||||
Set this to a 32-byte hexadecimal string to generate deterministic Node IDs
|
||||
when creating new profiles. For example, integration tests use the following
|
||||
setting:
|
||||
|
||||
RAD_KEYGEN_SEED=ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
|
||||
|
||||
**`RAD_PASSPHRASE`**
|
||||
|
||||
Set this to the passphrase chosen during profile initialization (`rad auth`) to
|
||||
skip the passphrase prompt. It's recommended to set this while developing to
|
||||
avoid storing development keys with `ssh-agent`.
|
||||
|
||||
## Logging
|
||||
|
||||
Logging for `radicle-node` is turned on by default. Check the respective
|
||||
`--help` output to set the log level.
|
||||
|
||||
## Writing tests
|
||||
|
||||
### Documentation tests
|
||||
|
||||
When implementing changes to the CLI, or adding a new sub-command, it's a good
|
||||
idea to add a documentation test. You can find examples of these in
|
||||
`radicle-cli/examples`.
|
||||
|
||||
Each documentation test must be accompanied by a regular unit test. These are
|
||||
located in `radicle-cli/tests/commands.rs`. To keep tests deterministic,
|
||||
environment variables are used. If your document test output is changing on
|
||||
each test run, make sure to account for any variability in the test environment
|
||||
(clocks, RNGs, etc.).
|
||||
|
||||
### Node service logic tests
|
||||
|
||||
When testing the core service logic, eg. the gossip protocol; tests can be
|
||||
added to `radicle-node/src/tests.rs`. These service-level tests simply test
|
||||
inputs and outputs and do not perform any I/O.
|
||||
|
||||
### Node end-to-end tests
|
||||
|
||||
If you find the need to test the replication protocol or networking layer, it's
|
||||
possible to write an end-to-end test. These tests can be found in
|
||||
`radicle-node/src/tests/e2e.rs`.
|
||||
|
||||
## Debugging
|
||||
|
||||
### Repository storage
|
||||
|
||||
Radicle stores git repositories inside `$RAD_HOME/storage`, which defaults to
|
||||
`~/.radicle/storage` on UNIX-based operating systems. You can use standard git
|
||||
tooling to inspect references and other git objects inside storage. Each radicle
|
||||
repository is stored under its own folder under storage as a bare Git repository.
|
||||
|
||||
Once inside a repository folder, the following commands may come in handy.
|
||||
|
||||
`git show-ref` to show all references:
|
||||
|
||||
$ git show-ref
|
||||
f60b291752bc38be7dfc90c4c4034de13e01a66b refs/heads/master
|
||||
f60b291752bc38be7dfc90c4c4034de13e01a66b refs/namespaces/z6MkqTY5aQepDGNCrkPqzdmzveX3D4oAmyVXUDDVQaDGdyVH/refs/heads/master
|
||||
805b7d0df927dcbc4d3911ab07cd497953eecbd1 refs/namespaces/z6MkqTY5aQepDGNCrkPqzdmzveX3D4oAmyVXUDDVQaDGdyVH/refs/rad/id
|
||||
86136a42a69572015466bac2d974154ee76f0853 refs/namespaces/z6MkqTY5aQepDGNCrkPqzdmzveX3D4oAmyVXUDDVQaDGdyVH/refs/rad/sigrefs
|
||||
5575035d8b4faf1f18c532b08516f18031dd7b28 refs/namespaces/z6MkuGSynjxM8SLhcsiEPWZgDeGLAVNXf5g7WePmc1Tri1FS/refs/heads/master
|
||||
805b7d0df927dcbc4d3911ab07cd497953eecbd1 refs/namespaces/z6MkuGSynjxM8SLhcsiEPWZgDeGLAVNXf5g7WePmc1Tri1FS/refs/rad/id
|
||||
89e4f0baa327595c7b2849189fc8808388a29033 refs/namespaces/z6MkuGSynjxM8SLhcsiEPWZgDeGLAVNXf5g7WePmc1Tri1FS/refs/rad/sigrefs
|
||||
|
||||
`git cat-file` to examine refs:
|
||||
|
||||
$ git cat-file -p f60b291752bc38be7dfc90c4c4034de13e01a66b
|
||||
|
||||
tree 1afc38724d2b89264c7b3826d40b0655a95cfab4
|
||||
author cloudhead <cloudhead@anonymous.xyz> 1678097961 +0100
|
||||
committer cloudhead <cloudhead@anonymous.xyz> 1678097961 +0100
|
||||
gpgsig -----BEGIN SSH SIGNATURE-----
|
||||
U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgvjrQogRxxLjzzWns8+mKJAGzEX
|
||||
4fm2ALoN7pyvD2ssQAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5
|
||||
AAAAQHXhUf7QjXNlgCjDbGSG+zoyIlE4S9/d9qjvG7x9jw8J/fXDVIMkh/Lkp743g7EliM
|
||||
X+88wqit9BeQoHXuxj2Ao=
|
||||
-----END SSH SIGNATURE-----
|
||||
|
||||
Init
|
||||
|
||||
You can also run `git ls-remote rad` from inside a working copy to examine the
|
||||
remote refs in storage.
|
||||
|
||||
### Connecting to your local node
|
||||
|
||||
The radicle node listens on a UNIX domain socket located at
|
||||
`$RAD_HOME/node/control.sock`. Make sure this file is accessible and has the
|
||||
required permissions for your user to read and write to it.
|
||||
|
||||
### Radicle keys
|
||||
|
||||
Radicle uses Ed25519 keys that are located in `$RAD_HOME/keys`. These keys are
|
||||
encoded in the standard OpenSSH format. It's therefore possible to use standard
|
||||
OpenSSH tools to interact with them, eg. `ssh-add`.
|
||||
|
||||
Your radicle secret key is protected with a passphrase (See: `$RAD_PASSPHRASE`).
|
||||
|
||||
-176
@@ -1,176 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 The Radicle Foundation
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,68 +0,0 @@
|
||||
# ❤️🪵
|
||||
|
||||
*Radicle Heartwood Protocol & Stack*
|
||||
|
||||
Heartwood is the third iteration of the Radicle Protocol, a powerful
|
||||
peer-to-peer code collaboration and publishing stack. The repository contains a
|
||||
full implementation of Heartwood, complete with a user-friendly command-line
|
||||
interface (`rad`) and network daemon (`radicle-node`).
|
||||
|
||||
Radicle was designed to be a secure, decentralized and powerful alternative to
|
||||
code forges such as GitHub and GitLab that preserves user sovereignty
|
||||
and freedom.
|
||||
|
||||
See the [Protocol Guide](https://radicle.xyz/guides/protocol) for an
|
||||
in-depth description of how Radicle works.
|
||||
|
||||
## Installation
|
||||
|
||||
**Requirements**
|
||||
|
||||
* *Linux* or *Unix* based operating system.
|
||||
* Git 2.34 or later
|
||||
* OpenSSH 9.1 or later with `ssh-agent`
|
||||
|
||||
### 📀 From binaries
|
||||
|
||||
> Requires `curl` and `tar`.
|
||||
|
||||
Run the following command to install the latest binary release:
|
||||
|
||||
curl -sSf https://radicle.xyz/install | sh
|
||||
|
||||
Or visit our [download](https://radicle.xyz/download) page.
|
||||
|
||||
### 📦 From source
|
||||
|
||||
> Requires the Rust toolchain.
|
||||
|
||||
You can install the Radicle stack from source, by running the following
|
||||
commands from inside this repository:
|
||||
|
||||
cargo install --path radicle-cli --force --locked --root ~/.radicle
|
||||
cargo install --path radicle-node --force --locked --root ~/.radicle
|
||||
cargo install --path radicle-remote-helper --force --locked --root ~/.radicle
|
||||
|
||||
Or directly from our seed node:
|
||||
|
||||
cargo install --force --locked --root ~/.radicle \
|
||||
--git https://seed.radicle.xyz/z3gqcJUoA1n9HaHKufZs5FCSGazv5.git \
|
||||
radicle-cli radicle-node radicle-remote-helper
|
||||
|
||||
## Running
|
||||
|
||||
*Systemd* unit files are provided for the node under the `/systemd` folder.
|
||||
They can be used as a starting point for further customization.
|
||||
|
||||
For running in debug mode, see [HACKING.md](HACKING.md).
|
||||
|
||||
## Contributing
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md) and [HACKING.md](HACKING.md) for an
|
||||
introduction to contributing to Radicle.
|
||||
|
||||
## License
|
||||
|
||||
Radicle is distributed under the terms of both the MIT license and the Apache License (Version 2.0).
|
||||
|
||||
See [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT) for details.
|
||||
@@ -1,60 +0,0 @@
|
||||
# Versioning
|
||||
|
||||
This document describes the versioning scheme used by the Radicle Stack, ie.
|
||||
the package that includes the Radicle CLI (`rad`), Radicle Node
|
||||
(`radicle-node`) and other binaries. It is not relevant to the Rust *crate*
|
||||
versions which follow [Semantic Versioning][semver].
|
||||
|
||||
[semver]: https://semver.org/
|
||||
|
||||
## Format
|
||||
|
||||
Versioning of the Radicle Stack is based on Git tags. During the build phase,
|
||||
we search for the most recent tag that starts with a `v` character, eg.
|
||||
`v1.0.0`, and use that as the basis for computing the version.
|
||||
|
||||
If we're building code that is pointed to by that tag directly, that code will
|
||||
inherit that version number, with the `v` character stripped. For example:
|
||||
|
||||
1.0.0
|
||||
|
||||
If on the other hand, the commit we are building has no version tag pointing to
|
||||
it, the output of `git describe` is used as-is. This indicates a development
|
||||
version that is not released and not meant to be packaged or distributed. For
|
||||
example:
|
||||
|
||||
1.0.0-6-ga3ffe51d
|
||||
|
||||
Tags used for versioning are always annotated and signed, and follow the format:
|
||||
|
||||
"v" <major> "." <minor> "." <patch>
|
||||
|
||||
When the tag is parsed, we strip the `v` prefix, which results in a version
|
||||
matching:
|
||||
|
||||
<major> "." <minor> "." <patch>
|
||||
|
||||
For pre-releases or release candidates, we add the `-rc` suffix, plus a number.
|
||||
For example:
|
||||
|
||||
1.0.0-rc.2
|
||||
|
||||
These releases are meant to be packaged.
|
||||
|
||||
## Semantics
|
||||
|
||||
The Radicle version numbers do not follow strict rules, but these are the
|
||||
guidelines we use:
|
||||
|
||||
1. Increment the `<major>` number when significant changes and/or improvements
|
||||
are made to the Radicle Stack. This should happen rarely.
|
||||
2. Increment the `<minor>` number when new features are added or existing
|
||||
features are improved in a noticeable way.
|
||||
3. Increment the `<patch>` number when bugs are fixed, docs are updated or
|
||||
features are tweaked.
|
||||
|
||||
Unless clearly stated in the documentation, none of the user-facing commands
|
||||
should be considered stable APIs, and may therefore break in `<minor>` or
|
||||
`<major>` version updates. If command output is considered stable, and an
|
||||
effort is made to maintain that stability, this will be stated in the command's
|
||||
documentation.
|
||||
@@ -1,54 +0,0 @@
|
||||
use std::env;
|
||||
use std::process::Command;
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Set a build-time `GIT_HEAD` env var which includes the commit id;
|
||||
// such that we can tell which code is running.
|
||||
let hash = env::var("GIT_HEAD").unwrap_or_else(|_| {
|
||||
Command::new("git")
|
||||
.arg("rev-parse")
|
||||
.arg("--short")
|
||||
.arg("HEAD")
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|output| {
|
||||
if output.status.success() {
|
||||
String::from_utf8(output.stdout).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or("unknown".into())
|
||||
});
|
||||
|
||||
let version = if let Ok(version) = env::var("RADICLE_VERSION") {
|
||||
version
|
||||
} else {
|
||||
"pre-release".to_owned()
|
||||
};
|
||||
|
||||
// Set a build-time `SOURCE_DATE_EPOCH` env var which includes the commit time.
|
||||
let commit_time = env::var("SOURCE_DATE_EPOCH").unwrap_or_else(|_| {
|
||||
Command::new("git")
|
||||
.arg("log")
|
||||
.arg("-1")
|
||||
.arg("--pretty=%ct")
|
||||
.arg("HEAD")
|
||||
.output()
|
||||
.ok()
|
||||
.and_then(|output| {
|
||||
if output.status.success() {
|
||||
String::from_utf8(output.stdout).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.unwrap_or(0.to_string())
|
||||
});
|
||||
|
||||
println!("cargo::rustc-env=RADICLE_VERSION={version}");
|
||||
println!("cargo::rustc-env=SOURCE_DATE_EPOCH={commit_time}");
|
||||
println!("cargo::rustc-env=GIT_HEAD={hash}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
# Builds release binaries for Radicle.
|
||||
FROM rust:1.77.2-alpine3.19 as builder
|
||||
LABEL maintainer="Radicle Team <team@radicle.xyz>"
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
|
||||
ARG TZ
|
||||
ARG LC_ALL
|
||||
ARG SOURCE_DATE_EPOCH
|
||||
ARG RADICLE_VERSION
|
||||
ARG GIT_HEAD
|
||||
|
||||
# Copy cargo configuration we're going to use to specify compiler options.
|
||||
RUN mkdir -p .cargo && cp build/config.toml .cargo/config.toml
|
||||
# Install dependencies.
|
||||
RUN apk update && apk add --no-cache git musl-dev minisign curl xz asciidoctor
|
||||
# Build man pages and strip metadata. Removes all comments, since they include
|
||||
# non-reproducible information, such as version numbers.
|
||||
RUN asciidoctor --doctype manpage --backend manpage --destination-dir . *.1.adoc && \
|
||||
find . -maxdepth 1 -type f -name '*.1' -exec sed -i '/^.\\\"/d' '{}' \;
|
||||
# Add cargo targets.
|
||||
RUN rustup target add \
|
||||
x86_64-unknown-linux-musl \
|
||||
aarch64-unknown-linux-musl \
|
||||
x86_64-apple-darwin \
|
||||
aarch64-apple-darwin
|
||||
|
||||
# Install dependencies for cross-compiling to macOS.
|
||||
# We use Zig as the linker to perform the compilation from a Linux host.
|
||||
# Zig is not yet available on Debian, so we download the official binary.
|
||||
# Compilation is done via `cargo-zigbuild` which is a wrapper around `zig`.
|
||||
RUN curl -sSf -o zig.tar.xz https://ziglang.org/download/0.12.0/zig-linux-x86_64-0.12.0.tar.xz && \
|
||||
curl -sSf -o zig.tar.xz.minisig https://ziglang.org/download/0.12.0/zig-linux-x86_64-0.12.0.tar.xz.minisig && \
|
||||
minisign -Vm zig.tar.xz -P RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U && \
|
||||
xz -d -c zig.tar.xz | tar -x && \
|
||||
mv zig-linux-x86_64-0.12.0/zig /usr/bin/zig && \
|
||||
mv zig-linux-x86_64-0.12.0/lib /usr/lib/zig && \
|
||||
cargo install cargo-zigbuild@0.18.3
|
||||
|
||||
|
||||
# Parts of the macOS SDK are required to build Radicle, we make these available
|
||||
# here. So far only `CoreFoundation` and `Security` frameworks are needed.
|
||||
RUN xz -d -c build/macos-sdk-11.3.tar.xz | tar -x
|
||||
# This env var is used by `cargo-zigbuild` to find the SDK.
|
||||
ENV SDKROOT /src/macos-sdk-11.3
|
||||
|
||||
# Build binaries.
|
||||
RUN cargo zigbuild --locked --release \
|
||||
--target=x86_64-apple-darwin \
|
||||
--target=aarch64-apple-darwin \
|
||||
--target=aarch64-unknown-linux-musl \
|
||||
--target=x86_64-unknown-linux-musl \
|
||||
-p radicle-node \
|
||||
-p radicle-remote-helper \
|
||||
-p radicle-cli
|
||||
|
||||
# Now copy the files to a new image without all the intermediary artifacts to
|
||||
# save some space.
|
||||
FROM alpine:3.19 as packager
|
||||
|
||||
ARG RADICLE_VERSION
|
||||
ARG SOURCE_DATE_EPOCH
|
||||
|
||||
COPY --from=builder \
|
||||
/src/target/x86_64-unknown-linux-musl/release/rad \
|
||||
/src/target/x86_64-unknown-linux-musl/release/git-remote-rad \
|
||||
/src/target/x86_64-unknown-linux-musl/release/radicle-node \
|
||||
/builds/x86_64-unknown-linux-musl/bin/
|
||||
COPY --from=builder \
|
||||
/src/target/aarch64-unknown-linux-musl/release/rad \
|
||||
/src/target/aarch64-unknown-linux-musl/release/git-remote-rad \
|
||||
/src/target/aarch64-unknown-linux-musl/release/radicle-node \
|
||||
/builds/aarch64-unknown-linux-musl/bin/
|
||||
COPY --from=builder \
|
||||
/src/target/aarch64-apple-darwin/release/rad \
|
||||
/src/target/aarch64-apple-darwin/release/git-remote-rad \
|
||||
/src/target/aarch64-apple-darwin/release/radicle-node \
|
||||
/builds/aarch64-apple-darwin/bin/
|
||||
COPY --from=builder \
|
||||
/src/target/x86_64-apple-darwin/release/rad \
|
||||
/src/target/x86_64-apple-darwin/release/git-remote-rad \
|
||||
/src/target/x86_64-apple-darwin/release/radicle-node \
|
||||
/builds/x86_64-apple-darwin/bin/
|
||||
COPY --from=builder /src/*.1 /builds/x86_64-unknown-linux-musl/man/man1/
|
||||
COPY --from=builder /src/*.1 /builds/aarch64-unknown-linux-musl/man/man1/
|
||||
COPY --from=builder /src/*.1 /builds/aarch64-apple-darwin/man/man1/
|
||||
COPY --from=builder /src/*.1 /builds/x86_64-apple-darwin/man/man1/
|
||||
|
||||
# Create and compress reproducible archive.
|
||||
WORKDIR /builds
|
||||
RUN x86_64-unknown-linux-musl/bin/rad version --json > radicle.json
|
||||
RUN apk update && apk add --no-cache tar xz
|
||||
RUN find * -maxdepth 0 -type d -exec mv '{}' "radicle-$RADICLE_VERSION-{}" \; && \
|
||||
find * -maxdepth 0 -type d -exec tar \
|
||||
--sort=name \
|
||||
--verbose \
|
||||
--mtime="@$SOURCE_DATE_EPOCH" \
|
||||
--owner=0 \
|
||||
--group=0 \
|
||||
--numeric-owner \
|
||||
--format=posix \
|
||||
--pax-option=exthdr.name=%d/PaxHeaders/%f,delete=atime,delete=ctime \
|
||||
--mode='go+u,go-w' \
|
||||
--remove-files \
|
||||
--create --xz \
|
||||
--file="{}.tar.xz" \
|
||||
'{}' \;
|
||||
@@ -1,70 +0,0 @@
|
||||
# Builds
|
||||
|
||||
Radicle uses a [reproducible build][rb] pipeline to make binary verification
|
||||
easier and more secure.
|
||||
|
||||
[rb]: https://reproducible-builds.org/
|
||||
|
||||
This build pipeline is designed to be run on an x86_64 machine running Linux.
|
||||
The output is a set of `.tar.xz` archives containing binaries for the supported
|
||||
platforms and signed by the user's Radicle key.
|
||||
|
||||
These binaries are statically linked to be maximally portable, and designed to
|
||||
be reproducible, byte for byte.
|
||||
|
||||
To run the build, simply enter the following command from the repository root:
|
||||
|
||||
build/build
|
||||
|
||||
This will build all targets and place the output in `build/artifacts` with
|
||||
one sub-directory per build target.
|
||||
|
||||
Note that it will use `git describe` to get a version number for the build.
|
||||
You *must* have a commit tagged with a version in your history or the build
|
||||
will fail, eg. `v1.0.0`.
|
||||
|
||||
When the build completes, the SHA-256 checksums of the artifacts are output.
|
||||
For a given Radicle version and source tree, the same set of checksums should
|
||||
always be output, no matter where or when the build is run. If they do not
|
||||
match, either the build pipeline has a bug, making it non-reproducible, or one
|
||||
of the machines is compromised.
|
||||
|
||||
Here's an example output for a development version of Radicle:
|
||||
|
||||
b9aa75bba175e18e05df4f6b39ec097414bbf56ccdeb4a2229b557f8b8e05404 radicle-1.0.0-rc.4-3-gb299f3b5-aarch64-apple-darwin.tar.xz
|
||||
c7070806bf2d17a8a0d3b329e4d57b1e544b7b82cb58e2863074d96348a2ab0d radicle-1.0.0-rc.4-3-gb299f3b5-aarch64-unknown-linux-musl.tar.xz
|
||||
1a8327854f16ea90491fb90e0c3291a63c4b2ab01742c8435faec7d370cacb79 radicle-1.0.0-rc.4-3-gb299f3b5-x86_64-apple-darwin.tar.xz
|
||||
709ac67541ff0c0c570ac22ab2de9f98320e0cc2cc9b67f1909c014a2bb5bd49 radicle-1.0.0-rc.4-3-gb299f3b5-x86_64-unknown-linux-musl.tar.xz
|
||||
|
||||
A script is included in `build/checksums` to output these checksums after
|
||||
the artifacts are built.
|
||||
|
||||
## Requirements
|
||||
|
||||
The following software is required for the build:
|
||||
|
||||
* `podman`
|
||||
* `rad` (The Radicle CLI)
|
||||
* `sha256sum`
|
||||
|
||||
## macOS
|
||||
|
||||
macOS binaries are not signed or notarized, so they have to be downloaded via
|
||||
the CLI to avoid issues. A copy of a small subset of the Apple SDK is included
|
||||
here to be able to cross-compile.
|
||||
|
||||
## Podman
|
||||
|
||||
We use `podman` to make the build reproducible on any machine by controlling
|
||||
the build environment. We prefer `podman` to `docker` because it doesn't
|
||||
require a background process to run and can be run without root access out of
|
||||
the box.
|
||||
|
||||
The first time you run `podman`, you may have to give yourself some extra UIDs
|
||||
for `podman` to use, with:
|
||||
|
||||
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
|
||||
|
||||
Then update `podman` with:
|
||||
|
||||
podman system migrate
|
||||
@@ -1,4 +0,0 @@
|
||||
x86_64-unknown-linux-musl
|
||||
aarch64-unknown-linux-musl
|
||||
x86_64-apple-darwin
|
||||
aarch64-apple-darwin
|
||||
-90
@@ -1,90 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
main() {
|
||||
# Use UTC time for everything.
|
||||
export TZ=UTC0
|
||||
# Set minimal locale.
|
||||
export LC_ALL=C
|
||||
# Set source date. This is honored by `asciidoctor` and other tools.
|
||||
export SOURCE_DATE_EPOCH=$(git log -1 --pretty=%ct)
|
||||
|
||||
if ! command -v rad > /dev/null; then
|
||||
echo "fatal: rad is not installed" >&2 ; exit 1
|
||||
fi
|
||||
|
||||
if ! command -v podman > /dev/null; then
|
||||
echo "fatal: podman is not installed" >&2 ; exit 1
|
||||
fi
|
||||
|
||||
if ! command -v sha256sum > /dev/null; then
|
||||
echo "fatal: sha256sum is not installed" >&2 ; exit 1
|
||||
fi
|
||||
|
||||
rev="$(git rev-parse --short HEAD)"
|
||||
tempdir="$(mktemp -d)"
|
||||
gitarchive="$tempdir/heartwood-$rev.tar.gz"
|
||||
keypath="$(rad path)/keys/radicle.pub"
|
||||
version="$(build/version)"
|
||||
image=radicle-build-$version
|
||||
|
||||
if [ ! -f "$keypath" ]; then
|
||||
echo "fatal: no key found at $keypath" >&2 ; exit 1
|
||||
fi
|
||||
# Authenticate user for signing
|
||||
rad auth
|
||||
|
||||
echo "Building Radicle $version.."
|
||||
echo "Creating archive of repository at $rev in $gitarchive.."
|
||||
git archive --format tar.gz -o "$gitarchive" HEAD
|
||||
|
||||
echo "Building image ($image).."
|
||||
podman --cgroup-manager=cgroupfs build \
|
||||
--build-arg SOURCE_DATE_EPOCH \
|
||||
--build-arg TZ \
|
||||
--build-arg LC_ALL \
|
||||
--build-arg RADICLE_VERSION=$version \
|
||||
--build-arg GIT_HEAD=$rev \
|
||||
--arch amd64 --tag $image -f ./build/Dockerfile - < $gitarchive
|
||||
|
||||
echo "Creating container (radicle-build-container).."
|
||||
podman --cgroup-manager=cgroupfs create --ulimit=host --replace --name radicle-build-container $image
|
||||
|
||||
# Copy build artifacts to output folder.
|
||||
outdir=build/artifacts
|
||||
mkdir -p $outdir
|
||||
podman cp --overwrite radicle-build-container:/builds/. $outdir/
|
||||
|
||||
for target in $(cat build/TARGETS); do
|
||||
echo "Signing artifacts for $target.."
|
||||
|
||||
filename="radicle-$version-$target.tar.xz"
|
||||
filepath="$outdir/$filename"
|
||||
|
||||
# Output SHA256 digest of archive.
|
||||
checksum="$(cd $outdir && sha256sum $filename)"
|
||||
echo "Checksum of $filepath is $(echo "$checksum" | cut -d' ' -f1)"
|
||||
echo "$checksum" > $filepath.sha256
|
||||
|
||||
# Sign archive and verify archive.
|
||||
rm -f $filepath.sig # Delete existing signature
|
||||
ssh-keygen -Y sign -n file -f $keypath $filepath
|
||||
ssh-keygen -Y check-novalidate -n file -s $filepath.sig < $filepath
|
||||
done
|
||||
|
||||
# Remove build artifacts that aren't needed anymore.
|
||||
rm -f $gitarchive
|
||||
podman rm radicle-build-container > /dev/null
|
||||
podman rmi --ignore localhost/$image
|
||||
}
|
||||
|
||||
# Run build.
|
||||
echo "Running build.."
|
||||
main "$@"
|
||||
|
||||
# Show artifact checksums.
|
||||
echo
|
||||
build/checksums
|
||||
echo
|
||||
|
||||
echo "Build successful."
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
find build/artifacts -type f -name '*.sha256' -exec cat {} +
|
||||
@@ -1,13 +0,0 @@
|
||||
[target.x86_64-unknown-linux-musl]
|
||||
rustflags = [
|
||||
"-C", "codegen-units=1",
|
||||
"-C", "incremental=false",
|
||||
"-C", "opt-level=3",
|
||||
]
|
||||
|
||||
[target.aarch64-unknown-linux-musl]
|
||||
rustflags = [
|
||||
"-C", "codegen-units=1",
|
||||
"-C", "incremental=false",
|
||||
"-C", "opt-level=3",
|
||||
]
|
||||
Binary file not shown.
@@ -1,38 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
if [ $# -ne 1 ]; then
|
||||
echo "Usage: $0 <version-number>"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
version="$1"
|
||||
tag="v$version"
|
||||
commit="$(git rev-parse HEAD)"
|
||||
signing_key=$(git config user.signingKey)
|
||||
|
||||
git show "$commit"
|
||||
|
||||
if [ "$signing_key" != "$(rad self --ssh-key)" ]; then
|
||||
echo "The Git signing key does not match the output of 'rad self --ssh-key'."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf "\n"
|
||||
printf "Tag the above commit with \033[35m$tag\033[0m, using \033[35m$(rad self --did)\033[0m? [y/N] "
|
||||
read confirmation
|
||||
rad auth
|
||||
|
||||
case "$confirmation" in
|
||||
[Yy]*)
|
||||
if git tag --annotate --sign "$tag" -m "Release $version" "$commit"; then
|
||||
echo "Tag $tag created and signed over $commit."
|
||||
else
|
||||
echo "Failed to create tag."
|
||||
exit 1
|
||||
fi ;;
|
||||
*)
|
||||
echo "Operation aborted."
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
SSH_LOGIN=${SSH_LOGIN:-release}
|
||||
SSH_ADDRESS=${SSH_ADDRESS:-$SSH_LOGIN@files.radicle.xyz}
|
||||
SSH_KEY="$(rad path)/keys/radicle"
|
||||
|
||||
main() {
|
||||
version="$(build/version)"
|
||||
|
||||
echo "Uploading Radicle $version..."
|
||||
|
||||
if [ -z "$version" ]; then
|
||||
echo "fatal: empty version number" >&2 ; exit 1
|
||||
fi
|
||||
|
||||
# Create remote folder.
|
||||
ssh -i $SSH_KEY $SSH_ADDRESS mkdir -p /mnt/radicle/files/releases/$version
|
||||
# Copy files over.
|
||||
scp -i $SSH_KEY build/artifacts/radicle-$version* $SSH_ADDRESS:/mnt/radicle/files/releases/$version
|
||||
scp -i $SSH_KEY build/artifacts/radicle.json $SSH_ADDRESS:/mnt/radicle/files/releases/$version
|
||||
|
||||
for target in $(cat build/TARGETS); do
|
||||
archive=/mnt/radicle/files/releases/$version/radicle-$version-$target.tar.xz
|
||||
symlink=/mnt/radicle/files/releases/$version/radicle-$target.tar.xz
|
||||
|
||||
echo "Creating symlinks for $target.."
|
||||
|
||||
ssh -i $SSH_KEY $SSH_ADDRESS ln -snf $archive $symlink
|
||||
ssh -i $SSH_KEY $SSH_ADDRESS ln -snf $archive.sig $symlink.sig
|
||||
ssh -i $SSH_KEY $SSH_ADDRESS ln -snf $archive.sha256 $symlink.sha256
|
||||
done
|
||||
|
||||
if git describe --exact-match --match='v*' 2>/dev/null; then
|
||||
echo "Creating 'latest' symlink.."
|
||||
ssh -i $SSH_KEY $SSH_ADDRESS ln -snf /mnt/radicle/files/releases/$version /mnt/radicle/files/releases/latest
|
||||
else
|
||||
echo "Skipping 'latest' symlink creation for development build."
|
||||
fi
|
||||
|
||||
echo "Pushing tags.."
|
||||
git push rad --tags
|
||||
|
||||
echo "Done."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
if ! version="$(git describe --match='v*' --candidates=1 2>/dev/null)"; then
|
||||
echo "fatal: no version tag found by 'git describe'" >&2 ; exit 1
|
||||
fi
|
||||
# Remove `v` prefix from version.
|
||||
version=${version#v}
|
||||
|
||||
echo $version
|
||||
Vendored
-34
@@ -1,34 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
rm -f ../radicle*_*
|
||||
git clean -fdx
|
||||
|
||||
# Get name and version of source package.
|
||||
name="$(dpkg-parsechangelog -SSource)"
|
||||
version="$(dpkg-parsechangelog -SVersion)"
|
||||
|
||||
# Get upstream version: everything before the last dash.
|
||||
uv="$(echo "$version" | sed 's/-[^-]*$//')"
|
||||
orig="${name}_${uv}.orig.tar.xz"
|
||||
|
||||
# Create a tar ball without the Debian packaging.
|
||||
tmp="$(mktemp -d)"
|
||||
trap 'rm -rf "$tmp"' EXIT
|
||||
git archive HEAD | tar -C "$tmp" -xf -
|
||||
rm -rf "$tmp/debian"
|
||||
tar -C "$tmp" -cf - . | xz >"../$orig"
|
||||
|
||||
# Actually build the package, without signing the .changes file.
|
||||
dpkg-buildpackage -us -uc
|
||||
|
||||
# Dump some information to make it easier to visually verify
|
||||
# everything looks OK. Also, test the package with the lintian tool.
|
||||
arch="$(dpkg --print-architecture)"
|
||||
deb="../${name}_${version}_${arch}.deb"
|
||||
changes="../${name}_${version}_${arch}.changes"
|
||||
|
||||
ls -l "$deb"
|
||||
dpkg -c "$deb"
|
||||
lintian -i "$changes"
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
/* This file needs to exist, but can be empty. */
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
radicle (0.8.0-1) unstable; urgency=medium
|
||||
|
||||
* New release.
|
||||
|
||||
-- Lars Wirzenius <liw@liw.fi> Mon, 25 Sep 2023 08:34:08 +0300
|
||||
Vendored
-2
@@ -1,2 +0,0 @@
|
||||
10
|
||||
|
||||
Vendored
-17
@@ -1,17 +0,0 @@
|
||||
Source: radicle
|
||||
Maintainer: The Radicle Team <team@radicle.xyz>
|
||||
Uploaders: Lars Wirzenius <liw@liw.fi>
|
||||
Section: vcs
|
||||
Priority: optional
|
||||
Standards-Version: 4.6.2
|
||||
Build-Depends: debhelper (>= 10~),
|
||||
asciidoctor
|
||||
Homepage: https://radicle.xyz/
|
||||
|
||||
Package: radicle
|
||||
Architecture: any
|
||||
Depends: ${misc:Depends}, ${shlibs:Depends}, git
|
||||
Description: peer-to-peer, distributed git hosting
|
||||
Radicle is a secure, decentralized and powerful alternative to code
|
||||
forges such as GitHub and GitLab that preserves user sovereignty and
|
||||
freedom.
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: heartwood
|
||||
Upstream-Contact: https://radicle.xyz/
|
||||
Source: https://app.radicle.xyz/seeds/seed.radicle.xyz
|
||||
|
||||
Files: *
|
||||
Copyright: 2019-2021, The Radicle Foundation
|
||||
License: Apache-2 or Expat
|
||||
License texts omitted, see LICENSE-APACHE and LICENSE-MIT in the
|
||||
source tree.
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
*.1 usr/share/man/man1
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
radicle binary: initial-upload-closes-no-bugs
|
||||
radicle binary: no-manual-page
|
||||
radicle binary: unstripped-binary-or-object
|
||||
Vendored
-18
@@ -1,18 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
DEB_BUILD_OPTIONS=nostrip
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
override_dh_auto_build:
|
||||
scripts/build-man-pages.sh . *.adoc
|
||||
|
||||
override_dh_auto_install:
|
||||
cargo install --locked --path=radicle-cli --root=debian/radicle
|
||||
cargo install --locked --path=radicle-node --root=debian/radicle
|
||||
cargo install --locked --path=radicle-remote-helper --root=debian/radicle
|
||||
rm -f debian/*/.crates*.*
|
||||
|
||||
override_dh_auto_test:
|
||||
echo tests are disabled, for now
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
3.0 (quilt)
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
radicle source: debian-rules-sets-DEB_BUILD_OPTIONS
|
||||
@@ -1,126 +0,0 @@
|
||||
# Note that all fields that take a lint level have these possible values:
|
||||
# * deny - An error will be produced and the check will fail
|
||||
# * warn - A warning will be produced, but the check will not fail
|
||||
# * allow - No warning or error will be produced
|
||||
|
||||
|
||||
# The graph table configures how the dependency graph is constructed and thus
|
||||
# which crates the checks are performed against
|
||||
[graph]
|
||||
targets = []
|
||||
# If true, metadata will be collected with `--all-features`. Note that this can't
|
||||
# be toggled off if true, if you want to conditionally enable `--all-features` it
|
||||
# is recommended to pass `--all-features` on the cmd line instead
|
||||
all-features = false
|
||||
# If true, metadata will be collected with `--no-default-features`. The same
|
||||
# caveat with `all-features` applies
|
||||
no-default-features = false
|
||||
|
||||
# The output table provides options for how/if diagnostics are outputted
|
||||
[output]
|
||||
# When outputting inclusion graphs in diagnostics that include features, this
|
||||
# option can be used to specify the depth at which feature edges will be added.
|
||||
# This option is included since the graphs can be quite large and the addition
|
||||
# of features from the crate(s) to all of the graph roots can be far too verbose.
|
||||
# This option can be overridden via `--feature-depth` on the cmd line
|
||||
feature-depth = 1
|
||||
|
||||
# This section is considered when running `cargo deny check advisories`
|
||||
[advisories]
|
||||
# Opt into new defaults since cargo deny 0.14.12 (2024-02-23)
|
||||
version = 2
|
||||
# The path where the advisory database is cloned/fetched into
|
||||
db-path = "~/.cargo/advisory-db"
|
||||
# The url(s) of the advisory databases to use
|
||||
db-urls = ["https://github.com/rustsec/advisory-db"]
|
||||
# The lint level for crates that have been yanked from their source registry.
|
||||
yanked = "warn"
|
||||
# A list of advisory IDs to ignore. Note that ignored advisories will still
|
||||
# output a note when they are encountered.
|
||||
ignore = []
|
||||
|
||||
# This section is considered when running `cargo deny check licenses`
|
||||
[licenses]
|
||||
# Opt into new defaults since cargo deny 0.14.12 (2024-02-23)
|
||||
version = 2
|
||||
# List of explicitly allowed licenses
|
||||
allow = [
|
||||
"MIT",
|
||||
"Apache-2.0",
|
||||
"Unicode-DFS-2016",
|
||||
"BSD-3-Clause",
|
||||
"MPL-2.0"
|
||||
]
|
||||
# The confidence threshold for detecting a license from license text.
|
||||
confidence-threshold = 0.8
|
||||
# Allow 1 or more licenses on a per-crate basis, so that particular licenses
|
||||
# aren't accepted for every possible crate as with the normal allow list
|
||||
exceptions = [
|
||||
{ allow = ["GPL-3.0"], name = "radicle-surf", version = "*" },
|
||||
{ allow = ["GPL-3.0"], name = "radicle-std-ext", version = "*" },
|
||||
{ allow = ["GPL-3.0"], name = "radicle-git-ext", version = "*" },
|
||||
{ allow = ["GPL-3.0"], name = "git-ref-format-core", version = "*" },
|
||||
{ allow = ["GPL-3.0"], name = "git-ref-format-macro", version = "*" },
|
||||
{ allow = ["GPL-3.0"], name = "git-ref-format", version = "*" },
|
||||
]
|
||||
|
||||
[licenses.private]
|
||||
# If true, ignores workspace crates that aren't published, or are only
|
||||
# published to private registries.
|
||||
ignore = false
|
||||
registries = []
|
||||
|
||||
# This section is considered when running `cargo deny check bans`.
|
||||
[bans]
|
||||
# Lint level for when multiple versions of the same crate are detected
|
||||
multiple-versions = "warn"
|
||||
# Lint level for when a crate version requirement is `*`
|
||||
wildcards = "allow"
|
||||
# The graph highlighting used when creating dotgraphs for crates
|
||||
# with multiple versions
|
||||
# * lowest-version - The path to the lowest versioned duplicate is highlighted
|
||||
# * simplest-path - The path to the version with the fewest edges is highlighted
|
||||
# * all - Both lowest-version and simplest-path are used
|
||||
highlight = "all"
|
||||
# The default lint level for `default` features for crates that are members of
|
||||
# the workspace that is being checked. This can be overridden by allowing/denying
|
||||
# `default` on a crate-by-crate basis if desired.
|
||||
workspace-default-features = "allow"
|
||||
# The default lint level for `default` features for external crates that are not
|
||||
# members of the workspace. This can be overridden by allowing/denying `default`
|
||||
# on a crate-by-crate basis if desired.
|
||||
external-default-features = "allow"
|
||||
# List of crates that are allowed. Use with care!
|
||||
allow = []
|
||||
# List of crates to deny
|
||||
deny = []
|
||||
|
||||
# Certain crates/versions that will be skipped when doing duplicate detection.
|
||||
skip = []
|
||||
# Similarly to `skip` allows you to skip certain crates during duplicate
|
||||
# detection. Unlike skip, it also includes the entire tree of transitive
|
||||
# dependencies starting at the specified crate, up to a certain depth, which is
|
||||
# by default infinite.
|
||||
skip-tree = []
|
||||
|
||||
# This section is considered when running `cargo deny check sources`.
|
||||
[sources]
|
||||
# Lint level for what to happen when a crate from a crate registry that is not
|
||||
# in the allow list is encountered
|
||||
unknown-registry = "warn"
|
||||
# Lint level for what to happen when a crate from a git repository that is not
|
||||
# in the allow list is encountered
|
||||
unknown-git = "warn"
|
||||
# List of URLs for allowed crate registries. Defaults to the crates.io index
|
||||
# if not specified. If it is specified but empty, no registries are allowed.
|
||||
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
||||
# List of URLs for allowed Git repositories
|
||||
allow-git = []
|
||||
|
||||
[sources.allow-org]
|
||||
# 1 or more github.com organizations to allow git sources for
|
||||
github = []
|
||||
# 1 or more gitlab.com organizations to allow git sources for
|
||||
gitlab = []
|
||||
# 1 or more bitbucket.org organizations to allow git sources for
|
||||
bitbucket = []
|
||||
Generated
-120
@@ -1,120 +0,0 @@
|
||||
{
|
||||
"nodes": {
|
||||
"advisory-db": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1722017365,
|
||||
"narHash": "sha256-9wYR5NZIgI+qzMDlJrUzevR31fvFQRgfjlYp50Xp3Ts=",
|
||||
"owner": "rustsec",
|
||||
"repo": "advisory-db",
|
||||
"rev": "9d024c07ee8c18609b43436bc865abf46636e250",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "rustsec",
|
||||
"repo": "advisory-db",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"crane": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1722907532,
|
||||
"narHash": "sha256-i/j/dMZPR7uJoQPFTa6UCahLu8kMtozYuR83lIKMERM=",
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"rev": "d0c8f4ed8571c533b31c1d68e4ebc534b790cc5c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "ipetkov",
|
||||
"repo": "crane",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1722940684,
|
||||
"narHash": "sha256-X8JnSq0ruRWsU4PdYuxV+8W4W66F1lnCcxIZZMWzo4E=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a3103d68517c6ad262ea27c96fc4a38ad81be7a0",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "release-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"advisory-db": "advisory-db",
|
||||
"crane": "crane",
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"rust-overlay": "rust-overlay"
|
||||
}
|
||||
},
|
||||
"rust-overlay": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1722910815,
|
||||
"narHash": "sha256-v6Vk/xlABhw2QzOa6xh3Jx/IvmlbKbOazFM+bDFQlWU=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "7df2ac544c203d21b63aac23bfaec7f9b919a733",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -1,242 +0,0 @@
|
||||
{
|
||||
description = "Radicle";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/release-24.05";
|
||||
|
||||
crane = {
|
||||
url = "github:ipetkov/crane";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
rust-overlay = {
|
||||
url = "github:oxalica/rust-overlay";
|
||||
inputs = {
|
||||
nixpkgs.follows = "nixpkgs";
|
||||
flake-utils.follows = "flake-utils";
|
||||
};
|
||||
};
|
||||
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
|
||||
advisory-db = {
|
||||
url = "github:rustsec/advisory-db";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
nixConfig = {
|
||||
keepOutputs = true;
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
crane,
|
||||
flake-utils,
|
||||
advisory-db,
|
||||
rust-overlay,
|
||||
...
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (system: let
|
||||
lib = nixpkgs.lib;
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
overlays = [(import rust-overlay)];
|
||||
};
|
||||
|
||||
rustToolChain = pkgs.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
|
||||
craneLib = (crane.mkLib pkgs).overrideToolchain rustToolChain;
|
||||
|
||||
srcFilters = path: type:
|
||||
builtins.any (suffix: lib.hasSuffix suffix path) [
|
||||
".sql" # schemas
|
||||
".diff" # testing
|
||||
".md" # testing
|
||||
".adoc" # man pages
|
||||
]
|
||||
||
|
||||
# Default filter from crane (allow .rs files)
|
||||
(craneLib.filterCargoSources path type);
|
||||
|
||||
src = lib.cleanSourceWith {
|
||||
src = ./.;
|
||||
filter = srcFilters;
|
||||
};
|
||||
|
||||
basicArgs = {
|
||||
inherit src;
|
||||
pname = "Heartwood";
|
||||
strictDeps = true;
|
||||
};
|
||||
|
||||
# Build *just* the cargo dependencies, so we can reuse
|
||||
# all of that work (e.g. via cachix) when running in CI
|
||||
cargoArtifacts = craneLib.buildDepsOnly basicArgs;
|
||||
|
||||
# Common arguments can be set here to avoid repeating them later
|
||||
commonArgs =
|
||||
basicArgs
|
||||
// {
|
||||
inherit cargoArtifacts;
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
git
|
||||
# Add additional build inputs here
|
||||
];
|
||||
buildInputs = lib.optionals pkgs.stdenv.buildPlatform.isDarwin (with pkgs; [
|
||||
darwin.apple_sdk.frameworks.Security
|
||||
]);
|
||||
|
||||
env =
|
||||
{
|
||||
RADICLE_VERSION = "nix-" + (self.shortRev or self.dirtyShortRev or "unknown");
|
||||
}
|
||||
// (
|
||||
if self ? rev || self ? dirtyRev
|
||||
then {
|
||||
GIT_HEAD = self.rev or self.dirtyRev;
|
||||
}
|
||||
else {}
|
||||
);
|
||||
};
|
||||
in {
|
||||
# Formatter
|
||||
formatter = pkgs.alejandra;
|
||||
|
||||
# Set of checks that are run: `nix flake check`
|
||||
checks = {
|
||||
# Build the crate as part of `nix flake check` for convenience
|
||||
inherit (self.packages.${system}) radicle;
|
||||
|
||||
# Run clippy (and deny all warnings) on the crate source,
|
||||
# again, reusing the dependency artifacts from above.
|
||||
#
|
||||
# Note that this is done as a separate derivation so that
|
||||
# we can block the CI if there are issues here, but not
|
||||
# prevent downstream consumers from building our crate by itself.
|
||||
clippy = craneLib.cargoClippy (commonArgs
|
||||
// {
|
||||
cargoClippyExtraArgs = "--all-targets -- --deny warnings";
|
||||
});
|
||||
|
||||
doc = craneLib.cargoDoc commonArgs;
|
||||
deny = craneLib.cargoDeny commonArgs;
|
||||
fmt = craneLib.cargoFmt basicArgs;
|
||||
|
||||
audit = craneLib.cargoAudit {
|
||||
inherit src advisory-db;
|
||||
};
|
||||
|
||||
# Run tests with cargo-nextest
|
||||
nextest = craneLib.cargoNextest (commonArgs
|
||||
// {
|
||||
partitions = 1;
|
||||
partitionType = "count";
|
||||
nativeBuildInputs = [
|
||||
# git is required so the sandbox can access it.
|
||||
pkgs.git
|
||||
# Ensure that `git-remote-rad` is present for testing.
|
||||
self.packages.${system}.radicle-remote-helper
|
||||
];
|
||||
# Ensure dev is used since we rely on env variables being
|
||||
# set in tests.
|
||||
env.CARGO_PROFILE = "dev";
|
||||
});
|
||||
};
|
||||
|
||||
packages = let
|
||||
crate = {
|
||||
name,
|
||||
pages ? [],
|
||||
}:
|
||||
craneLib.buildPackage (commonArgs
|
||||
// {
|
||||
inherit (craneLib.crateNameFromCargoToml {cargoToml = src + "/" + name + "/Cargo.toml";}) pname version;
|
||||
cargoExtraArgs = "-p ${name}";
|
||||
doCheck = false;
|
||||
|
||||
nativeBuildInputs = with pkgs; [asciidoctor installShellFiles];
|
||||
postInstall = ''
|
||||
for page in ${lib.escapeShellArgs pages}; do
|
||||
asciidoctor -d manpage -b manpage $page
|
||||
installManPage ''${page::-5}
|
||||
done
|
||||
'';
|
||||
});
|
||||
crates = builtins.listToAttrs (map
|
||||
({name, ...} @ package: lib.nameValuePair name (crate package))
|
||||
[
|
||||
{
|
||||
name = "radicle-cli";
|
||||
pages = [
|
||||
"rad.1.adoc"
|
||||
"rad-id.1.adoc"
|
||||
"rad-patch.1.adoc"
|
||||
];
|
||||
}
|
||||
{
|
||||
name = "radicle-remote-helper";
|
||||
pages = ["git-remote-rad.1.adoc"];
|
||||
}
|
||||
{
|
||||
name = "radicle-node";
|
||||
pages = ["radicle-node.1.adoc"];
|
||||
}
|
||||
]);
|
||||
in
|
||||
crates
|
||||
// rec {
|
||||
default = radicle;
|
||||
radicle = pkgs.buildEnv {
|
||||
name = "radicle";
|
||||
paths = with crates; [
|
||||
radicle-cli
|
||||
radicle-node
|
||||
radicle-remote-helper
|
||||
];
|
||||
};
|
||||
radicle-full = pkgs.buildEnv {
|
||||
name = "radicle-full";
|
||||
paths = builtins.attrValues crates;
|
||||
};
|
||||
};
|
||||
|
||||
apps.default = flake-utils.lib.mkApp {
|
||||
drv = self.packages.${system}.radicle;
|
||||
};
|
||||
|
||||
apps.radicle-full = flake-utils.lib.mkApp {
|
||||
name = "rad";
|
||||
drv = self.packages.${system}.radicle-full;
|
||||
};
|
||||
|
||||
apps.rad = flake-utils.lib.mkApp {
|
||||
name = "rad";
|
||||
drv = self.packages.${system}.radicle-cli;
|
||||
};
|
||||
|
||||
apps.git-remote-rad = flake-utils.lib.mkApp {
|
||||
name = "git-remote-rad";
|
||||
drv = self.packages.${system}.radicle-remote-helper;
|
||||
};
|
||||
|
||||
apps.radicle-node = flake-utils.lib.mkApp {
|
||||
name = "radicle-node";
|
||||
drv = self.packages.${system}.radicle-node;
|
||||
};
|
||||
|
||||
devShells.default = craneLib.devShell {
|
||||
# Extra inputs can be added here; cargo and rustc are provided by default.
|
||||
packages = with pkgs; [
|
||||
cargo-watch
|
||||
cargo-nextest
|
||||
ripgrep
|
||||
rust-analyzer
|
||||
sqlite
|
||||
];
|
||||
|
||||
env.RUST_SRC_PATH = "${rustToolChain}/lib/rustlib/src/rust/library";
|
||||
};
|
||||
});
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
= git-remote-rad(1)
|
||||
The Radicle Team <team@radicle.xyz>
|
||||
:doctype: manpage
|
||||
:revnumber: 0.8.0
|
||||
:revdate: 2023-10-12
|
||||
:mansource: rad {revnumber}
|
||||
:manmanual: Radicle CLI Manual
|
||||
|
||||
== Name
|
||||
|
||||
git-remote-rad - Git remote helper for Radicle
|
||||
|
||||
== Synopsis
|
||||
|
||||
*git-remote-rad* *
|
||||
|
||||
== Description
|
||||
|
||||
**git-remote-rad** is a **git**(1) remote helper program to allow git
|
||||
to interact with the local Radicle storage. git will use this program
|
||||
automatically, when using a remote repository with the "rad" protocol.
|
||||
It is never invoked directly.
|
||||
|
||||
== SEE ALSO ==
|
||||
|
||||
*git-remote-helpers*(1), *rad*(1)
|
||||
-170
@@ -1,170 +0,0 @@
|
||||
= rad-id(1)
|
||||
The Radicle Team <team@radicle.xyz>
|
||||
:doctype: manpage
|
||||
:revnumber: 1.0.0
|
||||
:revdate: 2024-04-22
|
||||
:mansource: rad {revnumber}
|
||||
:manmanual: Radicle CLI Manual
|
||||
|
||||
== Name
|
||||
|
||||
rad-id - Manage changes to a Radicle repository's identity.
|
||||
|
||||
== Synopsis
|
||||
|
||||
*rad id* [<option>...] +
|
||||
*rad id* _list_ [<option>...] +
|
||||
*rad id* _update_ [--title <string>] [--description <string>] <option>... +
|
||||
*rad id* _edit_ <revision-id> [--title <string>] [--description <string>] [<option>...] +
|
||||
*rad id* _show_ <revision-id> [<option>...] +
|
||||
*rad id* _accept_ | _reject_ <revision-id> [<option>...] +
|
||||
*rad id* _redact_ <revision-id> [<option>...]
|
||||
|
||||
== Description
|
||||
|
||||
The *rad id* command is used to manage and propose changes to the identity of a
|
||||
Radicle repository. Each repository has an associated identity document that
|
||||
contains metadata such as the repository name, description, and delegates. The
|
||||
identity document is versioned and changes to it must be signed by a quorum of
|
||||
delegates.
|
||||
|
||||
The identity document is stored as a _Canonical JSON_ document.
|
||||
|
||||
== Commands
|
||||
|
||||
With no arguments, *rad id* defaults to the _list_ command, showing the list of
|
||||
revisions to the identity of the current repository.
|
||||
|
||||
=== list
|
||||
|
||||
Lists all revisions to the identity document.
|
||||
|
||||
=== update
|
||||
|
||||
Proposes a new revision to the identity document. Revisions have a title and a
|
||||
description in addition to the proposed updated identity document, just like
|
||||
source code commits.
|
||||
|
||||
If a title and description are not provided on the command line, you will be
|
||||
prompted to enter one via your text editor.
|
||||
|
||||
Note that if you are the repository's only delegate, proposed changes will be
|
||||
automatically accepted and included into the identity document.
|
||||
|
||||
*--title* _<string>_::
|
||||
Set the title for the new revision.
|
||||
|
||||
*--description* _<string>_::
|
||||
Set the description for the new revision.
|
||||
|
||||
*--delegate* _<did>_::
|
||||
Update the identity by adding a new delegate, identified by their DID.
|
||||
|
||||
*--rescind* _<did>_::
|
||||
Update the identity by removing a delegate identified by their DID.
|
||||
|
||||
*--threshold* _<num>_::
|
||||
Update the identity by setting the number of delegates required to accept a
|
||||
revision.
|
||||
|
||||
*--visibility* _<private>_ | _<public>_::
|
||||
Update the identity by setting the repository visibility to private or public.
|
||||
|
||||
*--allow* _<did>_::
|
||||
Update the identity by giving a specific peer access to a private repository.
|
||||
|
||||
*--payload* _<id> <key> <val>_::
|
||||
Update the identity by setting metadata in one of the identity payloads.
|
||||
This can be used to update a repository's project name or description, for
|
||||
example. To delete a field from a payload, simply set it to *null*.
|
||||
|
||||
*--no-confirm*::
|
||||
Don't ask for confirmation before creating the revision.
|
||||
|
||||
=== edit
|
||||
|
||||
Edit an existing revision to the identity document. The revision must still be
|
||||
in the "active" state. The same options as for *update* are available. Note
|
||||
that this edits a proposed revision to the identity; to edit the identity
|
||||
document itself, use *update*.
|
||||
|
||||
=== show
|
||||
|
||||
Show a specific revision of the identity document.
|
||||
|
||||
=== accept
|
||||
|
||||
Accept a proposed revision to the identity document. The revision must be in
|
||||
the "active" state and the caller must be a delegate.
|
||||
|
||||
=== reject
|
||||
|
||||
Reject a proposed revision to the identity document. The revision must be in
|
||||
the "active" state and the caller must be a delegate.
|
||||
|
||||
=== redact
|
||||
|
||||
Redact an existing revision to the identity document. The revision must not be
|
||||
in the "accepted" state and the caller must be the author of the revision.
|
||||
|
||||
== Options
|
||||
|
||||
*--repo* _<rid>_::
|
||||
Specify the repository to operate on. Defaults to the current repository.
|
||||
|
||||
*--quiet*, *-q*::
|
||||
Suppress output.
|
||||
|
||||
*--help*::
|
||||
Print help information.
|
||||
|
||||
== Examples
|
||||
|
||||
=== Adding a delegate
|
||||
|
||||
To add a new delegate to a repository and update the threshold, use the
|
||||
*update* command:
|
||||
|
||||
$ rad id update --title "Add Bob" --description "Add Bob as a delegate" \
|
||||
--delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk \
|
||||
--threshold 2
|
||||
|
||||
This will create a new revision proposing to add the delegate identified by the
|
||||
given DID and set the threshold to `2`, meaning two delegates must sign off on
|
||||
future identity changes.
|
||||
|
||||
=== Changing repository visibility
|
||||
|
||||
To change a repository from public to private:
|
||||
|
||||
$ rad id update --visibility private
|
||||
|
||||
Note that this will require acceptance from a quorum of delegates to take
|
||||
effect.
|
||||
|
||||
=== Changing a repository payload
|
||||
|
||||
To change a repository's name and description, this is usually done through the
|
||||
*xyz.radicle.project* payload:
|
||||
|
||||
$ rad id update --title "Update title and description" \
|
||||
--description "Improve clarity" \
|
||||
--payload xyz.radicle.project name '"radicle-beans"' \
|
||||
--payload xyz.radicle.project description '"Tasty Radicle beans"'
|
||||
|
||||
Note that the values passed to *--payload*, eg. `"radicle-beans"` must be valid
|
||||
_JSON_ values. This means that strings should be double quoted, as in the
|
||||
example above.
|
||||
|
||||
=== Removing a delegate
|
||||
|
||||
To remove a delegate and update the threshold, use the *--rescind* option:
|
||||
|
||||
$ rad id update --title "Remove Bob" \
|
||||
--description "Bob is no longer a delegate" \
|
||||
--rescind did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk \
|
||||
--threshold 1
|
||||
|
||||
As with adding a delegate, this change will require approval from the remaining
|
||||
delegates. Make sure you set an appropriate new threshold when removing
|
||||
delegates!
|
||||
@@ -1,287 +0,0 @@
|
||||
= rad-patch(1)
|
||||
The Radicle Team <team@radicle.xyz>
|
||||
:doctype: manpage
|
||||
:revnumber: 1.0.0
|
||||
:revdate: 2024-04-22
|
||||
:mansource: rad {revnumber}
|
||||
:manmanual: Radicle CLI Manual
|
||||
|
||||
== Name
|
||||
|
||||
rad-patch - Manage radicle patches.
|
||||
|
||||
== Synopsis
|
||||
|
||||
*rad patch* [<option>...] +
|
||||
*rad patch* _list_ [--all|--merged|--open|--archived|--draft] [<option>...] +
|
||||
*rad patch* _show_ <patch-id> [<option>...] +
|
||||
*rad patch* _diff_ <patch-id> [--revision <revision-id>] [<option>...] +
|
||||
*rad patch* _archive_ <patch-id> [--undo] [<option>...] +
|
||||
*rad patch* _update_ <patch-id> [<option>...] +
|
||||
*rad patch* _checkout_ <patch-id> [<option>...] +
|
||||
*rad patch* _delete_ <patch-id> [<option>...] +
|
||||
*rad patch* _redact_ <revision-id> [<option>...] +
|
||||
*rad patch* _ready_ <patch-id> [--undo] [<option>...] +
|
||||
*rad patch* _review_ <patch-id> [<option>...] +
|
||||
*rad patch* _edit_ <patch-id> [<option>...] +
|
||||
*rad patch* _set_ <patch-id> [<option>...] +
|
||||
*rad patch* _comment_ <revision-id> [<option>...] +
|
||||
*rad patch* _label_ <patch-id> [<option>...] +
|
||||
|
||||
*git push rad* HEAD:refs/patches +
|
||||
|
||||
== Description
|
||||
|
||||
The Radicle *patch* command is used for managing changesets inside of Radicle
|
||||
repositories.
|
||||
|
||||
Though many actions can be performed using *rad patch*, certain patch-related
|
||||
actions use *git* directly. For example, opening a patch is typically
|
||||
done using *git push*, while merging a patch is done with a combination of
|
||||
*git merge* and *git push*.
|
||||
|
||||
To make this possible, Radicle ships with a helper program, *git-remote-rad*
|
||||
which is invoked by *git* on push and fetch to and from Radicle remotes.
|
||||
|
||||
== Commands
|
||||
|
||||
With no arguments, *rad patch* defaults to the _list_ command, showing the list of
|
||||
open patches for the current repository.
|
||||
|
||||
=== show
|
||||
|
||||
Shows information on the given patch.
|
||||
|
||||
*<patch-id>*:: The patch to show
|
||||
*--patch*, *-p*:: Show the patch changes in git patch format
|
||||
*--verbose*, *-v*:: Show additional information about the patch
|
||||
|
||||
=== diff
|
||||
|
||||
Outputs the patch diff, using Radicle's diffing tool.
|
||||
|
||||
*<patch-id>*:: The patch to diff
|
||||
*--revision*, *-r <revision-id>*:: The revision to diff (default: latest)
|
||||
|
||||
=== edit
|
||||
|
||||
Edits a patch revision comment. To edit the patch title or description, pass
|
||||
in the *<patch-id>*. To edit a revision comment, pass that revision's
|
||||
*<revision-id>*.
|
||||
|
||||
*<revision-id>*::
|
||||
The revision to edit.
|
||||
|
||||
*--message*, *-m [<string>]*::
|
||||
Comment message to the patch or revision. If omitted, Radicle will prompt for
|
||||
a comment string via *$EDITOR*.
|
||||
|
||||
=== list
|
||||
|
||||
List patches in the current repository. The default is *--open*.
|
||||
|
||||
*--all*:: List all patches, including merged and archived patches
|
||||
*--archived*:: List only archived patches
|
||||
*--merged*:: List only merged patches
|
||||
*--open*:: List only open patches
|
||||
*--draft*:: List only draft patches
|
||||
*--authored*:: Show only patches that you have authored
|
||||
*--author <did>*:: Show only patched where the given user is an author
|
||||
(may be specified multiple times)
|
||||
|
||||
=== ready
|
||||
|
||||
Mark a patch as ready to review. This changes the state of a patch from *draft*
|
||||
to *open*.
|
||||
|
||||
*--undo*:: Change a patch back to *draft*
|
||||
|
||||
=== review
|
||||
|
||||
Review a patch. Indicate acceptance or rejection of a patch revision along
|
||||
with a comment.
|
||||
|
||||
*--revision*, *-r <revision-id>*:: The revision to diff (default: latest)
|
||||
*--patch*, *--p*:: Review by patch hunks
|
||||
*--hunk <index>*:: Only review a specific hunk
|
||||
*--accept*:: Accept a patch or set of hunks
|
||||
*--reject*:: Reject a patch or set of hunks
|
||||
*--unified*, *-U <n>*:: Generate diffs with *<n>* lines of context instead of the usual three
|
||||
*--delete*, *-d*:: Delete a review draft
|
||||
*--message*, *-m [<string>]*:: Provide a comment with the review (default: prompt)
|
||||
|
||||
=== archive
|
||||
|
||||
Archive a patch.
|
||||
|
||||
*--undo*:: Unarchive a patch
|
||||
|
||||
=== set
|
||||
|
||||
Set the current branch upstream to a patch reference. This configures your
|
||||
branch such that pushing will update the specified patch.
|
||||
|
||||
*<patch-id>*:: The patch to set this branch's upstream to
|
||||
|
||||
=== update
|
||||
|
||||
Updates a patch to the current repository *HEAD*. This is a low-level command
|
||||
that should only be used when using *git push rad* is not possible.
|
||||
|
||||
*--message*, *-m [<string>]*:: Provide a comment message to the revision
|
||||
*--no-message*:: Leave the revision comment message blank
|
||||
|
||||
=== checkout
|
||||
|
||||
Switch to a given patch, by creating a branch that points to the patch head.
|
||||
This is essentially equivalent to *git checkout -b <name>* followed by
|
||||
*rad patch set <patch-id>*. By default, the branch name includes the Patch ID.
|
||||
|
||||
*--revision <id>*:: Checkout the given revision of the patch
|
||||
*--name <string>*:: Provide a name for the new branch
|
||||
*--force*, *-f*:: If the checkout already exists, update its head
|
||||
|
||||
=== comment
|
||||
|
||||
Comment on a patch revision, optionally replying to an existing comment.
|
||||
|
||||
*<revision-id>*::
|
||||
The patch revision to comment on. The Patch ID is also a Revision ID,
|
||||
and can be used for commenting on the initial revision of the patch.
|
||||
Any other Revision ID will comment on the revision specified.
|
||||
|
||||
*--message*, *-m <string>*::
|
||||
Comment message. If omitted, Radicle will prompt for a comment string via
|
||||
*$EDITOR*. Multiple messages will be concatinated with a blank line in between.
|
||||
|
||||
*--reply-to <comment-id>*::
|
||||
Optional comment to reply to. If ommitted, the comment is a top-level comment
|
||||
on the given revision.
|
||||
|
||||
== Opening a patch
|
||||
|
||||
To open a patch, we start by making changes to our working copy, typically on
|
||||
a feature branch. For example:
|
||||
|
||||
$ git checkout -b fix/option-parsing
|
||||
... edit some files ...
|
||||
$ git commit -a -m "Fix option parsing"
|
||||
|
||||
Once our changes are ready to be proposed as a patch, we push them via *git*
|
||||
to a special reference on the *rad* remote, that is used for opening patches
|
||||
(*refs/patches*):
|
||||
|
||||
$ git push rad HEAD:refs/patches
|
||||
✓ Patch 90c77f2c33b7e472e058de4a586156f8a7fec7d6 opened
|
||||
...
|
||||
|
||||
Radicle will then open your editor, where you can edit the patch title and
|
||||
description. Make sure either *EDITOR* or *VISUAL* is set in your environment
|
||||
(See *environ(7)* for more details). Once you're done, simply save and exit your
|
||||
editor. If successful, the patch is opened and its identifier is printed out.
|
||||
You can then display the patch metadata using the *show* sub-command:
|
||||
|
||||
$ rad patch show 90c77f2
|
||||
|
||||
Note that you don't have to use the full patch identifier. An unambiguous
|
||||
prefix of it also works.
|
||||
|
||||
Radicle can create a patch from any Git commit. Simply substitute *HEAD* with
|
||||
the branch name or commit hash you wish to propose a patch for. For example:
|
||||
|
||||
$ git push rad d39fe32387496876fae6446daf3762aacf69d83b:refs/patches
|
||||
|
||||
After the patch is opened, you may notice that Radicle has set your branch
|
||||
upstream to something like *rad/patches/90c77f2c33b7e472e058de4a586156f8a7fec7d6*.
|
||||
This means your branch is now associated with the newly opened patch, and any
|
||||
push from this branch will result in the patch being updated. See the next
|
||||
section on updating a patch for more information.
|
||||
|
||||
=== Options
|
||||
|
||||
When opening a patch, various options can be specified using git push options.
|
||||
This is done via the *-o* or *--push-option* flag. For example, *-o patch.draft*.
|
||||
The full list of options follows:
|
||||
|
||||
*sync*, *no-sync*::
|
||||
Whether or not to sync with the network after the patch is opened. Defaults
|
||||
to _sync_.
|
||||
|
||||
*sync.debug*::
|
||||
Show debug information about the syncing process.
|
||||
|
||||
*patch.draft*::
|
||||
Open the patch as a _draft_. Turned off by default.
|
||||
|
||||
*patch.message*=_<message>_::
|
||||
To prevent the editor from opening, you can specify the patch message via this
|
||||
option. Multiple *patch.message* options are concatenated with a blank line
|
||||
in between.
|
||||
|
||||
*patch.base*=_<oid>_::
|
||||
The base commit onto which this patch should be merged. By default, this is
|
||||
your "master" branch. When building stacked patches, it may be useful to
|
||||
set this to the head of a previous patch.
|
||||
|
||||
For more information on push options, see *git-push(1)*.
|
||||
|
||||
== Updating a patch
|
||||
|
||||
To update a patch, we simply make our changes locally and push:
|
||||
|
||||
$ git commit --amend
|
||||
$ git push --force
|
||||
✓ Patch 90c77f2 updated to revision d0018fcc21d87c91a1ff9155aed6b4e57535566b
|
||||
...
|
||||
|
||||
Note that this will only work if the current branch upstream is set correctly.
|
||||
This happens automatically when a patch is opened from a branch without an
|
||||
upstream set. In the above example, we used the *--force* option, since the
|
||||
commit was amended. This is common practice when a patch has been reworked
|
||||
after receiving a review.
|
||||
|
||||
If the branch upstream is not set to the patch reference, ie. *rad/patches/<id>*,
|
||||
you can do so using `rad patch set <id>`.
|
||||
|
||||
As with opening a patch, you will be asked to enter a reason for updating the
|
||||
patch, via your editor. Simply save and exit when you're done; or leave it
|
||||
blank to skip this step.
|
||||
|
||||
It's also possible to change the patch _base_ during an update. Simply use the
|
||||
*patch.base* push option as described in _Opening a patch_.
|
||||
|
||||
== Checking out a patch
|
||||
|
||||
When working with patches opened by peers, it's often useful to be able to
|
||||
checkout the code in its own branch. With a patch checkout, you can browse the
|
||||
code, run tests and even propose your own update to the patch. The *checkout*
|
||||
sub-command is used to that effect:
|
||||
|
||||
$ rad patch checkout 90c77f2
|
||||
|
||||
Radicle will create a new branch if necessary and checkout the patch head. From
|
||||
there, you can *git-push* to publish a patch update, or simply browse the code.
|
||||
|
||||
== Merging a patch
|
||||
|
||||
Once a patch is ready to merge, the repository maintainer simply has to use the
|
||||
*git-merge(1)* command from the "master" branch and push via *git*. For
|
||||
example, if some patch *26e3e56* is ready to merge, the steps would be:
|
||||
|
||||
$ rad patch checkout 26e3e56
|
||||
✓ Switched to branch patch/26e3e56
|
||||
$ git checkout master
|
||||
$ git merge patch/26e3e56
|
||||
$ git push rad
|
||||
✓ Patch 26e3e563ddc7df8dd0c9f81274c0b3cb1b764568 merged
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
f2de534..d6399c7 master -> master
|
||||
|
||||
In the above, we created a checkout for the patch, and merged that branch into
|
||||
our master branch. Then we pushed to our *rad* remote.
|
||||
|
||||
== Listing patches
|
||||
|
||||
To list patches, run *rad patch*. By default, this will only show open patches.
|
||||
To list all patches, including ones that have been merged or archived, add the
|
||||
*--all* option.
|
||||
-218
@@ -1,218 +0,0 @@
|
||||
= rad(1)
|
||||
The Radicle Team <team@radicle.xyz>
|
||||
:doctype: manpage
|
||||
:revnumber: 1.0.0
|
||||
:revdate: 2024-04-22
|
||||
:mansource: rad {revnumber}
|
||||
:manmanual: Radicle CLI Manual
|
||||
|
||||
== Name
|
||||
|
||||
rad - Radicle command-line tooling.
|
||||
|
||||
== Synopsis
|
||||
|
||||
*rad* *
|
||||
|
||||
== Description
|
||||
|
||||
The Radicle CLI is a set of tools designed for users of the Radicle
|
||||
peer-to-peer code collaboration stack. The CLI provides the ability to manage
|
||||
Radicle profiles and publish repositories to the Radicle network.
|
||||
|
||||
== Requirements
|
||||
|
||||
There are a few important requirements for running the Radicle CLI: first, a
|
||||
recent version of *git* (*2.34.0* or later) must be installed. You can check
|
||||
your installation by running *git --version*. Second, *ssh-agent* must be
|
||||
installed and running. To bypass ssh-agent, it's possible to set the
|
||||
*RAD_PASSPHRASE* environment variable. See the _Environment_ section.
|
||||
|
||||
== Getting started
|
||||
|
||||
Let's see how we can setup Radicle and clone a repository from the network.
|
||||
|
||||
Before we start, make sure that the *rad* binary is available on your system by
|
||||
running:
|
||||
|
||||
$ rad
|
||||
|
||||
This is the base command and the output will show you all available
|
||||
sub-commands.
|
||||
|
||||
=== Creating a profile
|
||||
|
||||
When using Radicle for the first time, you have to create a new Radicle
|
||||
_profile_. A profile is simply a cryptographic key pair associated with some
|
||||
storage space on your local device. Radicle stores everything under the Radicle
|
||||
_home_ directory, which defaults to *~/.radicle* on most systems.
|
||||
To create your profile, run:
|
||||
|
||||
$ rad auth
|
||||
|
||||
Follow the instructions by entering an alias and a passphrase to protect your
|
||||
key pair. When you've completed the steps, your new Radicle _DID_
|
||||
(Decentralized Identifier) will be displayed. This identifies your device, as
|
||||
well as all the code you publish. Share it freely with collaborators. Your
|
||||
alias is a non-unique name that is shared across the network to identify your
|
||||
node. It can always be changed at a later time.
|
||||
|
||||
After running *rad auth*, you should have a new profile and key pair under your
|
||||
Radicle home. The key pair is used for identifying peers across the network, as
|
||||
well as signing code and other artifacts published on the network.
|
||||
|
||||
To avoid having to re-enter your passphrase every time a signature is required,
|
||||
*rad auth* adds your secret key to *ssh-agent*.
|
||||
|
||||
You can always display the full path to your Radicle home directory using:
|
||||
|
||||
$ rad self --home
|
||||
|
||||
The *self* sub-command is useful for querying profile information. You can
|
||||
run it without any arguments to show all of your profile information, including
|
||||
your DID:
|
||||
|
||||
$ rad self
|
||||
|
||||
=== Connecting to the network
|
||||
|
||||
Radicle is a peer-to-peer system. This means that there are no clients or
|
||||
servers; only _nodes_, and every user has to run their own. This ensures
|
||||
that there is no single point of failure, and the Radicle network can keep on
|
||||
existing as long as there are users to run nodes.
|
||||
|
||||
The Radicle node is a lightweight process that runs in the background and
|
||||
manages connections with other nodes, as well as data replication. To check your
|
||||
node's status, run:
|
||||
|
||||
$ rad node status
|
||||
|
||||
If the node is not running, you can start it with:
|
||||
|
||||
$ rad node start
|
||||
|
||||
This will start the node in the foreground. Simply add *--daemon* to run the
|
||||
node in the background.
|
||||
|
||||
When daemonized, the node will log all output to a file under your Radicle home.
|
||||
On most systems, this will be *~/.radicle/node/node.log*.
|
||||
|
||||
You can also display the latest logs at any time using:
|
||||
|
||||
$ rad node logs
|
||||
|
||||
After your node starts, it will attempt to connect to peers on the network.
|
||||
|
||||
To connect to a specific peer, the *connect* sub-command can be used:
|
||||
For example, to connect to the *radicle.garden* community node, you can run:
|
||||
|
||||
$ rad node connect z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776
|
||||
|
||||
The argument given to *connect* is called a node _address_. It is composed of
|
||||
a Node ID (NID), followed by an *@* symbol, and the _host_ name and _port_.
|
||||
You can display your NID with *rad self --nid*.
|
||||
|
||||
To make these connections permanent, such that they are always tried on
|
||||
startup, add the addresses to your configuration file at
|
||||
*~/.radicle/config.json*, under the *"connect"* field.
|
||||
|
||||
Now that you're connected to the network, we can try to clone a repository
|
||||
hosted on Radicle.
|
||||
|
||||
=== Cloning a repository
|
||||
|
||||
To clone a repository from the Radicle network, you need to be connected to a
|
||||
seed which is hosting the repository. Eventually, this won't be necessary,
|
||||
but for now, a direct connection with the seed you are cloning from is
|
||||
required. Once the connection is established, you can run the *rad clone*
|
||||
command to clone a repository, by supplying a Repository Identifier (RID).
|
||||
RIDs are globally unique URNs that identify a repository on Radicle.
|
||||
For example, let's clone the Radicle heartwood repository:
|
||||
|
||||
$ rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5
|
||||
|
||||
If the command succeeds, you will have a checkout of the repository under
|
||||
*./heartwood*, with a *rad* remote setup, which you can pull from and push to.
|
||||
|
||||
The *rad* remote is special in that it will always return the _authoritative_
|
||||
branch of the repo, when fetched from. This is defined as the latest commit
|
||||
on the repository's default branch that a threshold of delegates have published.
|
||||
|
||||
Under the hood, the *clone* command does a few important things:
|
||||
|
||||
* It subscribes to updates from this repository; this is called _tracking_.
|
||||
* It creates a copy of the repository under your local storage, which provides
|
||||
a replica to the network.
|
||||
* It creates a Git remote for every repository _delegate_. These are usually
|
||||
the repository maintainers.
|
||||
* It creates a fork of the repository that is under your public key.
|
||||
* It creates a *rad* remote which you can push to, to update your fork.
|
||||
|
||||
You can see all replicated and locally-available repositories using:
|
||||
|
||||
$ rad ls --all
|
||||
|
||||
By default, your node will subscribe to updates from _all_ peers. This
|
||||
behavior can be changed by passing *--scope followed* when cloning. It can
|
||||
also be changed later via the *rad seed* command.
|
||||
|
||||
=== Publishing a repository on Radicle
|
||||
|
||||
To publish an existing Git repository on the Radicle network, navigate to
|
||||
a working copy and run the *rad init* command:
|
||||
|
||||
$ cd my-repo
|
||||
$ rad init
|
||||
|
||||
You will be prompted to enter a repository name and default branch. Once the
|
||||
repository is created, the Repository Identifier (RID) will be printed.
|
||||
Share this with collaborators so that they can clone your repository.
|
||||
|
||||
Note that your new repository will only be replicated by nodes that you are
|
||||
connected to and either have an open tracking policy, or trust your Node ID.
|
||||
|
||||
The *rad init* command creates a special remote in your working copy named
|
||||
*rad*. Pushing to this remote publishes changes to your local storage and to
|
||||
the network.
|
||||
|
||||
You can view information about the *rad* remote with:
|
||||
|
||||
$ git remote show rad
|
||||
|
||||
You can display the Repository ID (RID) from the root of any Git repository
|
||||
by running:
|
||||
|
||||
$ rad .
|
||||
|
||||
You can also show the repository's identity payload, which contains its name,
|
||||
description and default branch by running:
|
||||
|
||||
$ rad inspect --payload
|
||||
|
||||
== Files
|
||||
|
||||
*~/.radicle*::
|
||||
|
||||
The _Radicle Home_. This is the default location for Radicle configuration
|
||||
files, keys, and repository storage. Can be controlled by settting the
|
||||
**RAD_HOME** environment variable.
|
||||
|
||||
*~/.radicle/config.json*::
|
||||
|
||||
The default configuration file for Radicle. To display its exact
|
||||
location on your system, you can use the *rad self --config* command.
|
||||
|
||||
== Environment
|
||||
|
||||
Various environment variables are used by *rad*.
|
||||
|
||||
*RAD_HOME*::
|
||||
|
||||
This sets the location of your Radicle home. If unset, your Radicle home will
|
||||
be located at *~/.radicle*.
|
||||
|
||||
*RAD_PASSPHRASE*::
|
||||
|
||||
If you would like to bypass *ssh-agent*, you can store your passphrase in this
|
||||
environment variable. Note that this is not secure and is equivalent to having
|
||||
an unencrypted secret key.
|
||||
@@ -1,24 +0,0 @@
|
||||
[package]
|
||||
name = "radicle-cli-test"
|
||||
description = "Radicle CLI test library"
|
||||
homepage = "https://radicle.xyz"
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.10.0"
|
||||
authors = ["cloudhead <cloudhead@radicle.xyz>"]
|
||||
edition = "2021"
|
||||
categories = ["development-tools::testing"]
|
||||
keywords = ["cli", "test", "command"]
|
||||
readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
escargot = { version = "0.5.7" }
|
||||
log = { version = "0.4", features = ["std"] }
|
||||
pretty_assertions = { version = "1.3.0" }
|
||||
shlex = { version = "1.1.0" }
|
||||
snapbox = { version = "0.4.3" }
|
||||
thiserror = { version = "1" }
|
||||
|
||||
[dependencies.radicle]
|
||||
path = "../radicle"
|
||||
version = "0"
|
||||
features = ["logger", "test"]
|
||||
@@ -1,35 +0,0 @@
|
||||
# radicle-cli-test
|
||||
|
||||
Test your CLI with the help of markdown descriptions.
|
||||
|
||||
## Example
|
||||
|
||||
Test flows are described in markdown like this example:
|
||||
|
||||
````` markdown
|
||||
# Echoing works
|
||||
|
||||
When I call echo, it answers:
|
||||
|
||||
```
|
||||
$ echo "ohai"
|
||||
ohai
|
||||
```
|
||||
`````
|
||||
|
||||
Say this is placed in `kind-echo.md`, this is what the corresponding test case
|
||||
would look lke:
|
||||
|
||||
``` rust
|
||||
use std::path::Path;
|
||||
use radicle_cli_test::TestFormula;
|
||||
|
||||
#[test]
|
||||
fn kind_echo() {
|
||||
TestFormula::new()
|
||||
.file(Path::new("./kind-echo.md"))
|
||||
.unwrap()
|
||||
.run()
|
||||
.unwrap();
|
||||
}
|
||||
```
|
||||
@@ -1,659 +0,0 @@
|
||||
#![allow(clippy::collapsible_else_if)]
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
use std::sync;
|
||||
use std::{env, ffi, fs, io, mem};
|
||||
|
||||
use snapbox::cmd::{Command, OutputAssert};
|
||||
use snapbox::{Assert, Substitutions};
|
||||
use thiserror::Error;
|
||||
|
||||
/// Used to ensure the build task is only run once.
|
||||
static BUILD: sync::Once = sync::Once::new();
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error("parsing failed")]
|
||||
Parse,
|
||||
#[error("invalid file path: {0:?}")]
|
||||
InvalidFilePath(String),
|
||||
#[error("unknown home {0:?}")]
|
||||
UnknownHome(String),
|
||||
#[error("test file not found: {0:?}")]
|
||||
TestNotFound(PathBuf),
|
||||
#[error("i/o: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
#[error("snapbox: {0}")]
|
||||
Snapbox(#[from] snapbox::Error),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum ExitStatus {
|
||||
Success,
|
||||
Failure,
|
||||
}
|
||||
|
||||
/// A test which may contain multiple assertions.
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct Test {
|
||||
/// Human-readable context around the test. Functions as documentation.
|
||||
context: Vec<String>,
|
||||
/// Test assertions to run.
|
||||
assertions: Vec<Assertion>,
|
||||
/// Whether to check stderr's output instead of stdout.
|
||||
stderr: bool,
|
||||
/// Whether to expect an error status code.
|
||||
fail: bool,
|
||||
/// Home directory under which to run this test.
|
||||
home: Option<String>,
|
||||
/// Local env vars to use just for this test.
|
||||
env: HashMap<String, String>,
|
||||
}
|
||||
|
||||
/// An assertion is a command to run with an expected output.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Assertion {
|
||||
/// The test file that contains this assertion.
|
||||
path: PathBuf,
|
||||
/// Name of command to run, eg. `git`.
|
||||
command: String,
|
||||
/// Command arguments, eg. `["push"]`.
|
||||
args: Vec<String>,
|
||||
/// Expected output (stdout or stderr).
|
||||
expected: String,
|
||||
/// Expected exit status.
|
||||
exit: ExitStatus,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, Clone)]
|
||||
pub struct Home {
|
||||
name: Option<String>,
|
||||
path: PathBuf,
|
||||
envs: HashMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TestRun {
|
||||
home: Home,
|
||||
env: HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl TestRun {
|
||||
fn cd(&mut self, path: PathBuf) {
|
||||
self.home.path = path;
|
||||
}
|
||||
|
||||
fn envs(&self) -> impl Iterator<Item = (String, String)> + '_ {
|
||||
self.home
|
||||
.envs
|
||||
.iter()
|
||||
.chain(self.env.iter())
|
||||
.map(|(k, v)| (k.to_owned(), v.to_owned()))
|
||||
.chain(Some((
|
||||
"PWD".to_owned(),
|
||||
self.home.path.to_string_lossy().to_string(),
|
||||
)))
|
||||
}
|
||||
|
||||
fn path(&self) -> PathBuf {
|
||||
self.home.path.clone()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct TestRunner<'a> {
|
||||
cwd: Option<PathBuf>,
|
||||
homes: HashMap<String, Home>,
|
||||
formula: &'a TestFormula,
|
||||
}
|
||||
|
||||
impl<'a> TestRunner<'a> {
|
||||
fn new(formula: &'a TestFormula) -> Self {
|
||||
Self {
|
||||
cwd: None,
|
||||
homes: formula.homes.clone(),
|
||||
formula,
|
||||
}
|
||||
}
|
||||
|
||||
fn run(&mut self, test: &'a Test) -> TestRun {
|
||||
let mut env = self.formula.env.clone();
|
||||
env.extend(test.env.clone());
|
||||
|
||||
if let Some(ref h) = test.home {
|
||||
if let Some(home) = self.homes.get(h) {
|
||||
return TestRun {
|
||||
home: home.clone(),
|
||||
env,
|
||||
};
|
||||
} else {
|
||||
panic!("TestRunner::test: home `~{h}` does not exist");
|
||||
}
|
||||
}
|
||||
TestRun {
|
||||
home: Home {
|
||||
name: None,
|
||||
path: self.cwd.clone().unwrap_or_else(|| self.formula.cwd.clone()),
|
||||
envs: HashMap::new(),
|
||||
},
|
||||
env,
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(&mut self, run: TestRun) {
|
||||
if let Some(name) = &run.home.name {
|
||||
self.homes.insert(name.clone(), run.home);
|
||||
} else {
|
||||
self.cwd = Some(run.home.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq)]
|
||||
pub struct TestFormula {
|
||||
/// Current working directory to run the test in.
|
||||
cwd: PathBuf,
|
||||
/// User homes.
|
||||
homes: HashMap<String, Home>,
|
||||
/// Environment to pass to the test.
|
||||
env: HashMap<String, String>,
|
||||
/// Tests to run.
|
||||
tests: Vec<Test>,
|
||||
/// Output substitutions.
|
||||
subs: Substitutions,
|
||||
/// Binaries path.
|
||||
bins: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl TestFormula {
|
||||
pub fn new(cwd: PathBuf) -> Self {
|
||||
Self {
|
||||
cwd,
|
||||
env: HashMap::new(),
|
||||
homes: HashMap::new(),
|
||||
tests: Vec::new(),
|
||||
subs: Substitutions::new(),
|
||||
bins: env::var("PATH")
|
||||
.map(|p| p.split(':').map(PathBuf::from).collect())
|
||||
.unwrap_or_default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(&mut self, binaries: &[(&str, &str)]) -> &mut Self {
|
||||
let manifest = env::var("CARGO_MANIFEST_DIR").expect(
|
||||
"TestFormula::build: cannot build binaries: variable `CARGO_MANIFEST_DIR` is not set",
|
||||
);
|
||||
let profile = if cfg!(debug_assertions) {
|
||||
"debug"
|
||||
} else {
|
||||
"release"
|
||||
};
|
||||
let target_dir = env::var("CARGO_TARGET_DIR").unwrap_or("target".to_string());
|
||||
let manifest = Path::new(manifest.as_str());
|
||||
let bins = manifest.join(&target_dir).join(profile);
|
||||
|
||||
// Add the target dir to the beginning of the list we will use as `PATH`.
|
||||
self.bins.insert(0, bins);
|
||||
|
||||
// We don't need to re-build everytime the `build` function is called. Once is enough.
|
||||
BUILD.call_once(|| {
|
||||
use escargot::format::Message;
|
||||
use radicle::logger::env_level;
|
||||
use radicle::logger::test as logger;
|
||||
|
||||
logger::init(env_level().unwrap_or(log::Level::Debug));
|
||||
|
||||
for (package, binary) in binaries {
|
||||
log::debug!(target: "test", "Building binaries for package `{package}`..");
|
||||
|
||||
let results = escargot::CargoBuild::new()
|
||||
.package(package)
|
||||
.bin(binary)
|
||||
.manifest_path(manifest.join("Cargo.toml"))
|
||||
.target_dir(&target_dir)
|
||||
.exec()
|
||||
.unwrap();
|
||||
|
||||
for result in results {
|
||||
match result {
|
||||
Ok(msg) => {
|
||||
if let Ok(Message::CompilerArtifact(a)) = msg.decode() {
|
||||
if let Some(e) = a.executable {
|
||||
log::debug!(target: "test", "Built {}", e.display());
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
log::error!(target: "test", "Error building package `{package}`: {e}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
self
|
||||
}
|
||||
|
||||
pub fn env(&mut self, key: impl ToString, val: impl ToString) -> &mut Self {
|
||||
self.env.insert(key.to_string(), val.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn home(
|
||||
&mut self,
|
||||
user: impl ToString,
|
||||
path: impl AsRef<Path>,
|
||||
envs: impl IntoIterator<Item = (impl ToString, impl ToString)>,
|
||||
) -> &mut Self {
|
||||
self.homes.insert(
|
||||
user.to_string(),
|
||||
Home {
|
||||
name: Some(user.to_string()),
|
||||
path: path.as_ref().to_path_buf(),
|
||||
envs: envs
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect(),
|
||||
},
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn envs<K: ToString, V: ToString>(
|
||||
&mut self,
|
||||
envs: impl IntoIterator<Item = (K, V)>,
|
||||
) -> &mut Self {
|
||||
for (k, v) in envs {
|
||||
self.env.insert(k.to_string(), v.to_string());
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub fn file(&mut self, path: impl AsRef<Path>) -> Result<&mut Self, Error> {
|
||||
let path = path.as_ref();
|
||||
let contents = match fs::read(path) {
|
||||
Ok(bytes) => bytes,
|
||||
Err(err) if err.kind() == io::ErrorKind::NotFound => {
|
||||
return Err(Error::TestNotFound(path.to_path_buf()));
|
||||
}
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
self.read(path, io::Cursor::new(contents))
|
||||
}
|
||||
|
||||
pub fn read(&mut self, path: &Path, r: impl io::BufRead) -> Result<&mut Self, Error> {
|
||||
let mut test = Test::default();
|
||||
let mut fenced = false; // Whether we're inside a fenced code block.
|
||||
let mut file: Option<(PathBuf, String)> = None; // Path and content of file created by this test block.
|
||||
|
||||
for line in r.lines() {
|
||||
let line = line?;
|
||||
|
||||
if line.starts_with("```") {
|
||||
if fenced {
|
||||
if let Some((ref path, ref mut content)) = file.take() {
|
||||
// Write file.
|
||||
let path = self.cwd.join(path);
|
||||
|
||||
if let Some(dir) = path.parent() {
|
||||
log::debug!(target: "test", "Creating directory {}..", dir.display());
|
||||
fs::create_dir_all(dir)?;
|
||||
}
|
||||
log::debug!(target: "test", "Writing {} bytes to {}..", content.len(), path.display());
|
||||
fs::write(path, content)?;
|
||||
} else {
|
||||
// End existing code block.
|
||||
self.tests.push(mem::take(&mut test));
|
||||
}
|
||||
} else {
|
||||
for token in line.split_whitespace() {
|
||||
if let Some(home) = token.strip_prefix('~') {
|
||||
test.home = Some(home.to_owned());
|
||||
} else if let Some((key, val)) = token.split_once('=') {
|
||||
test.env.insert(key.to_owned(), val.to_owned());
|
||||
} else if token.contains("stderr") {
|
||||
test.stderr = true;
|
||||
} else if token.contains("fail") {
|
||||
test.fail = true;
|
||||
} else if let Some(path) = token.strip_prefix("./") {
|
||||
file = Some((
|
||||
PathBuf::from_str(path)
|
||||
.map_err(|_| Error::InvalidFilePath(token.to_owned()))?,
|
||||
String::new(),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
fenced = !fenced;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if fenced {
|
||||
if let Some((_, ref mut content)) = file {
|
||||
content.push_str(line.as_str());
|
||||
content.push('\n');
|
||||
} else if let Some(line) = line.strip_prefix('$') {
|
||||
let line = line.trim();
|
||||
let parts = shlex::split(line).ok_or(Error::Parse)?;
|
||||
let (cmd, args) = parts.split_first().ok_or(Error::Parse)?;
|
||||
|
||||
test.assertions.push(Assertion {
|
||||
path: path.to_path_buf(),
|
||||
command: cmd.to_owned(),
|
||||
args: args.to_owned(),
|
||||
expected: String::new(),
|
||||
exit: if test.fail {
|
||||
ExitStatus::Failure
|
||||
} else {
|
||||
ExitStatus::Success
|
||||
},
|
||||
});
|
||||
} else if let Some(a) = test.assertions.last_mut() {
|
||||
a.expected.push_str(line.as_str());
|
||||
a.expected.push('\n');
|
||||
} else {
|
||||
return Err(Error::Parse);
|
||||
}
|
||||
} else {
|
||||
test.context.push(line);
|
||||
}
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn substitute(
|
||||
&mut self,
|
||||
value: &'static str,
|
||||
other: impl Into<Cow<'static, str>>,
|
||||
) -> Result<&mut Self, Error> {
|
||||
self.subs.insert(value, other)?;
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
/// Convert instances of '[.. ]' to '[..]' where the number of ' 's are arbitrary.
|
||||
///
|
||||
/// Supporting these bracket types help support using the '[..]' pattern while preserving
|
||||
/// spaces important for text alignment.
|
||||
fn map_spaced_brackets(s: &str) -> String {
|
||||
let mut ret = String::new();
|
||||
let mut pos = 0;
|
||||
|
||||
for c in s.chars() {
|
||||
match (c, pos) {
|
||||
('[', 0) => pos += 1,
|
||||
(' ', 1) => continue,
|
||||
('.', 1) => pos += 1,
|
||||
('.', 2) => pos += 1,
|
||||
('.', 3) => continue,
|
||||
(' ', 3) => continue,
|
||||
(']', 3) => pos = 0,
|
||||
(_, _) => pos = 0,
|
||||
}
|
||||
ret.push(c);
|
||||
}
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<bool, io::Error> {
|
||||
let assert = Assert::new().substitutions(self.subs.clone());
|
||||
let mut runner = TestRunner::new(self);
|
||||
|
||||
fs::create_dir_all(&self.cwd)?;
|
||||
log::debug!(target: "test", "Using PATH {:?}", self.bins);
|
||||
|
||||
// For each code block.
|
||||
for test in &self.tests {
|
||||
let mut run = runner.run(test);
|
||||
|
||||
// For each command.
|
||||
for assertion in &test.assertions {
|
||||
// Expand environment variables.
|
||||
let mut args = assertion.args.clone();
|
||||
for arg in &mut args {
|
||||
for (k, v) in run.envs() {
|
||||
*arg = arg.replace(format!("${k}").as_str(), &v);
|
||||
}
|
||||
}
|
||||
let path = assertion
|
||||
.path
|
||||
.file_name()
|
||||
.map(|f| f.to_string_lossy().to_string())
|
||||
.unwrap_or(String::from("<none>"));
|
||||
let cmd = if assertion.command == "rad" {
|
||||
snapbox::cmd::cargo_bin("rad")
|
||||
} else if assertion.command == "cd" {
|
||||
let arg = assertion.args.first().unwrap();
|
||||
let dir: PathBuf = arg.into();
|
||||
let dir = run.path().join(dir);
|
||||
|
||||
// TODO: Add support for `..` and `/`
|
||||
// TODO: Error if more than one args are given.
|
||||
|
||||
log::debug!(target: "test", "{path}: Running `cd {}`..", dir.display());
|
||||
|
||||
if !dir.exists() {
|
||||
return Err(io::Error::new(
|
||||
io::ErrorKind::NotFound,
|
||||
format!("cd: '{}' does not exist", dir.display()),
|
||||
));
|
||||
}
|
||||
run.cd(dir);
|
||||
|
||||
continue;
|
||||
} else {
|
||||
PathBuf::from(&assertion.command)
|
||||
};
|
||||
log::debug!(target: "test", "{path}: Running `{}` with {:?} in `{}`..", cmd.display(), assertion.args, run.path().display());
|
||||
|
||||
if !run.path().exists() {
|
||||
log::warn!(target: "test", "{path}: Directory {} does not exist. Creating..", run.path().display());
|
||||
fs::create_dir_all(run.path())?;
|
||||
}
|
||||
|
||||
let bins = self
|
||||
.bins
|
||||
.iter()
|
||||
.map(|p| p.as_os_str())
|
||||
.collect::<Vec<_>>()
|
||||
.join(ffi::OsStr::new(":"));
|
||||
let result = Command::new(cmd.clone())
|
||||
.env_clear()
|
||||
.env("PATH", &bins)
|
||||
.env("RUST_BACKTRACE", "1")
|
||||
.envs(run.envs())
|
||||
.current_dir(run.path())
|
||||
.args(args)
|
||||
.with_assert(assert.clone())
|
||||
.output();
|
||||
|
||||
match result {
|
||||
Ok(output) => {
|
||||
let assert = OutputAssert::new(output).with_assert(assert.clone());
|
||||
let expected = Self::map_spaced_brackets(&assertion.expected);
|
||||
|
||||
let matches = if test.stderr {
|
||||
assert.stderr_matches(&expected)
|
||||
} else {
|
||||
assert.stdout_matches(&expected)
|
||||
};
|
||||
match assertion.exit {
|
||||
ExitStatus::Success => {
|
||||
matches.success();
|
||||
}
|
||||
ExitStatus::Failure => {
|
||||
matches.failure();
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if err.kind() == io::ErrorKind::NotFound {
|
||||
log::error!(target: "test", "{path}: Command `{}` does not exist..", cmd.display());
|
||||
}
|
||||
return Err(io::Error::new(
|
||||
err.kind(),
|
||||
format!("{path}: {err}: `{}`", cmd.display()),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
runner.finish(run);
|
||||
}
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let input = r#"
|
||||
Let's try to track @dave and @sean:
|
||||
``` RAD_HINT=true
|
||||
$ rad track @dave
|
||||
Tracking relationship established for @dave.
|
||||
Nothing to do.
|
||||
|
||||
$ rad track @sean
|
||||
Tracking relationship established for @sean.
|
||||
Nothing to do.
|
||||
```
|
||||
Super, now let's move on to the next step.
|
||||
``` ~alice (stderr)
|
||||
$ rad sync
|
||||
```
|
||||
"#
|
||||
.trim()
|
||||
.as_bytes()
|
||||
.to_owned();
|
||||
|
||||
let mut actual = TestFormula::new(PathBuf::new());
|
||||
let path = Path::new("test.md").to_path_buf();
|
||||
actual
|
||||
.read(path.as_path(), io::BufReader::new(io::Cursor::new(input)))
|
||||
.unwrap();
|
||||
|
||||
let expected = TestFormula {
|
||||
homes: HashMap::new(),
|
||||
cwd: PathBuf::new(),
|
||||
env: HashMap::new(),
|
||||
subs: Substitutions::new(),
|
||||
bins: env::var("PATH")
|
||||
.unwrap()
|
||||
.split(':')
|
||||
.map(PathBuf::from)
|
||||
.collect(),
|
||||
tests: vec![
|
||||
Test {
|
||||
context: vec![String::from("Let's try to track @dave and @sean:")],
|
||||
home: None,
|
||||
assertions: vec![
|
||||
Assertion {
|
||||
path: path.clone(),
|
||||
command: String::from("rad"),
|
||||
args: vec![String::from("track"), String::from("@dave")],
|
||||
expected: String::from(
|
||||
"Tracking relationship established for @dave.\nNothing to do.\n\n",
|
||||
),
|
||||
exit: ExitStatus::Success,
|
||||
},
|
||||
Assertion {
|
||||
path: path.clone(),
|
||||
command: String::from("rad"),
|
||||
args: vec![String::from("track"), String::from("@sean")],
|
||||
expected: String::from(
|
||||
"Tracking relationship established for @sean.\nNothing to do.\n",
|
||||
),
|
||||
exit: ExitStatus::Success,
|
||||
},
|
||||
],
|
||||
fail: false,
|
||||
stderr: false,
|
||||
env: vec![("RAD_HINT".to_owned(), "true".to_owned())]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
},
|
||||
Test {
|
||||
context: vec![String::from("Super, now let's move on to the next step.")],
|
||||
home: Some("alice".to_owned()),
|
||||
assertions: vec![Assertion {
|
||||
path: path.clone(),
|
||||
command: String::from("rad"),
|
||||
args: vec![String::from("sync")],
|
||||
expected: String::new(),
|
||||
exit: ExitStatus::Success,
|
||||
}],
|
||||
fail: false,
|
||||
stderr: true,
|
||||
env: HashMap::default(),
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_run() {
|
||||
let input = r#"
|
||||
Running a simple command such as `head`:
|
||||
```
|
||||
$ head -n 2 Cargo.toml
|
||||
[package]
|
||||
name = "radicle-cli-test"
|
||||
```
|
||||
"#
|
||||
.trim()
|
||||
.as_bytes()
|
||||
.to_owned();
|
||||
|
||||
let mut formula = TestFormula::new(PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap());
|
||||
formula
|
||||
.read(
|
||||
Path::new("test.md"),
|
||||
io::BufReader::new(io::Cursor::new(input)),
|
||||
)
|
||||
.unwrap();
|
||||
formula.run().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_example_spaced_brackets() {
|
||||
let input = r#"
|
||||
Running a simple command such as `head`:
|
||||
```
|
||||
$ echo " hello"
|
||||
[..]hello
|
||||
$ echo " hello"
|
||||
[.. ]hello
|
||||
$ echo " hello"
|
||||
[ ..]hello
|
||||
$ echo "[bug, good-first-issue]"
|
||||
[bug, good-first-issue]
|
||||
$ echo "[bug, good-first-issue]"
|
||||
[bug, [ .. ]-issue]
|
||||
$ echo "[bug, good-first-issue]"
|
||||
[bug, [ ... ]-issue]
|
||||
```
|
||||
"#
|
||||
.trim()
|
||||
.as_bytes()
|
||||
.to_owned();
|
||||
|
||||
let mut formula = TestFormula::new(PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap());
|
||||
formula
|
||||
.read(
|
||||
Path::new("test.md"),
|
||||
io::BufReader::new(io::Cursor::new(input)),
|
||||
)
|
||||
.unwrap();
|
||||
formula.run().unwrap();
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
[package]
|
||||
name = "radicle-cli"
|
||||
description = "Radicle CLI"
|
||||
homepage = "https://radicle.xyz"
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.11.0"
|
||||
authors = ["cloudhead <cloudhead@radicle.xyz>"]
|
||||
edition = "2021"
|
||||
build = "build.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "rad"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
anyhow = { version = "1" }
|
||||
chrono = { version = "0.4.26", default-features = false, features = ["clock", "std"] }
|
||||
git-ref-format = { version = "0.3.0", features = ["macro"] }
|
||||
lexopt = { version = "0.3.0" }
|
||||
localtime = { version = "1.2.0" }
|
||||
log = { version = "0.4", features = ["std"] }
|
||||
nonempty = { version = "0.9.0" }
|
||||
# N.b. this is required to use macros, even though it's re-exported
|
||||
# through radicle
|
||||
radicle-git-ext = { version = "0.8.0", features = ["serde"] }
|
||||
radicle-surf = { version = "0.22.0" }
|
||||
serde = { version = "1.0" }
|
||||
serde_json = { version = "1" }
|
||||
shlex = { version = "1.1.0" }
|
||||
tempfile = { version = "3.3.0" }
|
||||
thiserror = { version = "1" }
|
||||
timeago = { version = "0.4.2", default-features = false }
|
||||
tree-sitter = { version = "0.20.0" }
|
||||
tree-sitter-highlight = { version = "0.20" }
|
||||
tree-sitter-json = { version = "0.20.1" }
|
||||
tree-sitter-rust = { version = "0.20" }
|
||||
tree-sitter-typescript = { version = "0.20.3" }
|
||||
tree-sitter-html = { version = "0.20" }
|
||||
tree-sitter-css = { version = "0.20" }
|
||||
tree-sitter-toml = { version = "0.20" }
|
||||
tree-sitter-c = { version = "0.20" }
|
||||
tree-sitter-python = { version = "0.20" }
|
||||
tree-sitter-ruby = { version = "0.20" }
|
||||
tree-sitter-bash = { version = "0.20" }
|
||||
tree-sitter-go = { version = "0.20.0" }
|
||||
tree-sitter-md = { version = "0.1.5" }
|
||||
zeroize = { version = "1.1" }
|
||||
|
||||
[dependencies.radicle]
|
||||
version = "0"
|
||||
path = "../radicle"
|
||||
features = ["logger"]
|
||||
|
||||
[dependencies.radicle-cli-test]
|
||||
version = "0"
|
||||
path = "../radicle-cli-test"
|
||||
|
||||
[dependencies.radicle-cob]
|
||||
version = "0"
|
||||
path = "../radicle-cob"
|
||||
|
||||
[dependencies.radicle-crypto]
|
||||
version = "0"
|
||||
path = "../radicle-crypto"
|
||||
|
||||
[dependencies.radicle-term]
|
||||
version = "0"
|
||||
path = "../radicle-term"
|
||||
|
||||
[dev-dependencies]
|
||||
pretty_assertions = { version = "1.3.0" }
|
||||
radicle = { version = "0", path = "../radicle", features = ["test"] }
|
||||
radicle-node = { path = "../radicle-node", features = ["test"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -1 +0,0 @@
|
||||
../build.rs
|
||||
@@ -1,33 +0,0 @@
|
||||
```
|
||||
$ mkdir bin
|
||||
$ cd bin
|
||||
$ touch file.bin
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ rad self --did
|
||||
did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
$ pwd
|
||||
[..]/home/bob/.radicle
|
||||
$ mkdir src
|
||||
$ cd src
|
||||
$ pwd
|
||||
[..]/home/bob/.radicle/src
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad self --did
|
||||
did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
$ pwd
|
||||
[..]/home/alice/.radicle
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ pwd
|
||||
[..]/home/bob/.radicle/src
|
||||
```
|
||||
|
||||
```
|
||||
$ ls
|
||||
file.bin
|
||||
```
|
||||
@@ -1,30 +0,0 @@
|
||||
```
|
||||
$ cd heartwood
|
||||
$ git branch -r
|
||||
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master
|
||||
rad/master
|
||||
```
|
||||
|
||||
```
|
||||
$ git ls-remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi 'refs/heads/*'
|
||||
145e1e69bef3ad93d14946ea212249c2fa9b9828 refs/heads/alice/1
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
|
||||
```
|
||||
|
||||
``` (stderr)
|
||||
$ git fetch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
* [new branch] alice/1 -> alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/alice/1
|
||||
```
|
||||
|
||||
```
|
||||
$ git branch -r
|
||||
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/alice/1
|
||||
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master
|
||||
rad/master
|
||||
```
|
||||
|
||||
```
|
||||
$ git rev-parse alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/alice/1
|
||||
145e1e69bef3ad93d14946ea212249c2fa9b9828
|
||||
```
|
||||
@@ -1,27 +0,0 @@
|
||||
``` ~alice
|
||||
$ rad id update --title "Add Bob" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji -q
|
||||
c036c0d89ce26aef3ad7da402157dba16b5163b4
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ rad sync --fetch
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ git commit -m "New changes" --allow-empty -q
|
||||
$ git push rad master -o no-sync
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ git commit --amend -m "Neue Änderungen" --allow-empty -q
|
||||
```
|
||||
|
||||
``` ~alice (stderr)
|
||||
$ git push rad master -f
|
||||
✓ Canonical head updated to 9170c8795d3a78f0381a0ffafb20ea69fb0f5b6b
|
||||
✓ Synced with 1 node(s)
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
+ fb25886...9170c87 master -> master (forced update)
|
||||
```
|
||||
@@ -1,142 +0,0 @@
|
||||
In this scenario we check that we can easily reset our canonical head to the
|
||||
head of another delegate when there is divergence between the 3 delegates.
|
||||
|
||||
First we add our new delegates, Bob & Eve, to our repo, while also setting the
|
||||
`threshold` to `3`:
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --title "Add Bob & Eve" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --delegate did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z --threshold 3 --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji -q
|
||||
3143236b2e40338f5574ec04e935a5ab80a6868a
|
||||
```
|
||||
|
||||
Bob and Eve will fetch the changes to ensure they hear about their delegate
|
||||
responsibilities:
|
||||
|
||||
``` ~bob
|
||||
$ rad sync --fetch
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkux1…nVhib7Z..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 2 seed(s)
|
||||
```
|
||||
|
||||
``` ~eve
|
||||
$ rad sync --fetch
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 2 seed(s)
|
||||
```
|
||||
|
||||
To demonstrate the divergence, Alice, Bob, and Eve will all create a new change,
|
||||
pushing to their `rad` remote -- but they won't sync to the network just yet:
|
||||
|
||||
``` ~alice
|
||||
$ git commit -m "Alice's commit" --allow-empty -q
|
||||
$ git push rad -o no-sync
|
||||
$ git ls-remote rad
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ git add README
|
||||
$ git commit -m "Bob's commit" -q
|
||||
$ git push rad -o no-sync
|
||||
$ git ls-remote rad
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
|
||||
```
|
||||
|
||||
``` ~eve
|
||||
$ git add README
|
||||
$ git commit -m "Eve's commit" -q
|
||||
$ git push rad -o no-sync
|
||||
$ git ls-remote rad
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
|
||||
```
|
||||
|
||||
Alice adds Bob and Eve as remotes and starts to notice that the `no quorum was
|
||||
found` error is showing up:
|
||||
|
||||
``` ~alice
|
||||
$ rad remote add did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob
|
||||
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkux1…nVhib7Z..
|
||||
✗ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk.. error: no quorum was found
|
||||
✓ Remote bob added
|
||||
✓ Remote-tracking branch bob/master created for z6Mkt67…v4N1tRk
|
||||
$ rad remote add did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z --name eve
|
||||
✓ Follow policy updated for z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z (eve)
|
||||
✗ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkux1…nVhib7Z.. error: no quorum was found
|
||||
✗ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk.. error: no quorum was found
|
||||
✓ Remote eve added
|
||||
✓ Remote-tracking branch eve/master created for z6Mkux1…nVhib7Z
|
||||
```
|
||||
|
||||
Alice does indeed have Bob and Eve's references, however, a new canonical
|
||||
`refs/heads/master` cannot be decided while they're out of quorum. This can be
|
||||
remedied by the delegates agreeing upon which way to move forward. In this case,
|
||||
Alice resets her `master` to `bob/master`:
|
||||
|
||||
``` ~alice
|
||||
$ git merge bob/master
|
||||
Merge made by the 'ort' strategy.
|
||||
README | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
$ git merge eve/master -s ours
|
||||
Merge made by the 'ours' strategy.
|
||||
```
|
||||
|
||||
She can then force push to update the canonical head to the new agreed upon
|
||||
commit:
|
||||
|
||||
``` ~alice (stderr)
|
||||
$ git push rad -f
|
||||
warn: no quorum was found for `refs/heads/master`
|
||||
warn: it is recommended to find a commit to agree upon
|
||||
✓ Synced with 1 node(s)
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
d09e634..0f9bd80 master -> master
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ rad remote add did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --name alice
|
||||
✓ Follow policy updated for z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
|
||||
✗ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkux1…nVhib7Z.. error: no quorum was found
|
||||
✗ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi.. error: no quorum was found
|
||||
✓ Remote alice added
|
||||
✓ Remote-tracking branch alice/master created for z6MknSL…StBU8Vi
|
||||
$ git reset --hard alice/master
|
||||
HEAD is now at 0f9bd80 Merge remote-tracking branch 'eve/master'
|
||||
```
|
||||
|
||||
When we check Bob's `rad` remote, we see that the commit for `refs/heads/master`
|
||||
has changed. This is actually Eve's commit as part of Alice merging above. Now
|
||||
that Alice, Bob, and Eve all have this commit as part of their history it has
|
||||
become the canonical `master`.
|
||||
|
||||
``` ~bob (stderr)
|
||||
$ git push rad
|
||||
✓ Canonical head updated to 3a75f66dd0020c9a0355cc6ec21f15de989e2001
|
||||
✓ Synced with 2 node(s)
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
2a37862..0f9bd80 master -> master
|
||||
```
|
||||
|
||||
Once Eve also resets to the merge commits, the canonical `master` is set to this tip.
|
||||
|
||||
``` ~eve
|
||||
$ rad remote add did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --name alice
|
||||
✓ Follow policy updated for z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Remote alice added
|
||||
✓ Remote-tracking branch alice/master created for z6MknSL…StBU8Vi
|
||||
$ git reset --hard alice/master
|
||||
HEAD is now at 0f9bd80 Merge remote-tracking branch 'eve/master'
|
||||
```
|
||||
|
||||
``` ~eve (stderr)
|
||||
$ git push rad
|
||||
✓ Canonical head updated to 0f9bd8035c04b3f73f5408e73e8454879b20800b
|
||||
✓ Synced with 2 node(s)
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z
|
||||
3a75f66..0f9bd80 master -> master
|
||||
```
|
||||
@@ -1,20 +0,0 @@
|
||||
Finally, we can also delete branches with `git push`:
|
||||
|
||||
```
|
||||
$ git ls-remote rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi refs/heads/*
|
||||
145e1e69bef3ad93d14946ea212249c2fa9b9828 refs/heads/alice/1
|
||||
ddcc1f164eacfd7dba41da9bff3261da3ee79fd3 refs/heads/alice/2
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
|
||||
```
|
||||
|
||||
``` (stderr) RAD_SOCKET=/dev/null
|
||||
$ git push rad :alice/1
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
- [deleted] alice/1
|
||||
```
|
||||
|
||||
```
|
||||
$ git ls-remote rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi refs/heads/*
|
||||
ddcc1f164eacfd7dba41da9bff3261da3ee79fd3 refs/heads/alice/2
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
|
||||
```
|
||||
@@ -1,80 +0,0 @@
|
||||
|
||||
Let's see what happens if we try to push a head which diverges from the
|
||||
canonical head.
|
||||
|
||||
First we add a second delegate, Bob, to our repo:
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --title "Add Bob" --description "" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji -q
|
||||
c036c0d89ce26aef3ad7da402157dba16b5163b4
|
||||
```
|
||||
|
||||
Then, as Bob, we commit some code on top of the canonical head:
|
||||
|
||||
``` ~bob
|
||||
$ rad sync --fetch
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
$ rad inspect --delegates
|
||||
did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
|
||||
did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
|
||||
$ git commit -m "Third commit" --allow-empty -q
|
||||
$ git push rad
|
||||
$ git branch -arv
|
||||
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master f2de534 Second commit
|
||||
rad/master 319a7dc Third commit
|
||||
```
|
||||
|
||||
As Alice, we fetch that code, but commit on top of our own master, which is no
|
||||
longer canonical, since Bob pushed a more recent commit, and the threshold is 1:
|
||||
|
||||
``` ~alice
|
||||
$ rad remote add did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --fetch --no-sync
|
||||
✓ Remote bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk added
|
||||
✓ Remote-tracking branch bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk/master created for z6Mkt67…v4N1tRk
|
||||
$ git branch -arv
|
||||
bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk/master 319a7dc Third commit
|
||||
rad/master f2de534 Second commit
|
||||
$ git commit -m "Third commit by Alice" --allow-empty -q
|
||||
```
|
||||
|
||||
If we try to push now, we get an error with a hint, telling us that we need to
|
||||
integrate Bob's changes before pushing ours:
|
||||
|
||||
``` ~alice (stderr) (fail) RAD_HINT=1
|
||||
$ git push rad
|
||||
hint: you are attempting to push a commit that would cause your upstream to diverge from the canonical head
|
||||
hint: to integrate the remote changes, run `git pull --rebase` and try again
|
||||
error: refusing to update branch to commit that is not a descendant of canonical head
|
||||
error: failed to push some refs to 'rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi'
|
||||
```
|
||||
|
||||
We do that, and notice that we're now able to push our code:
|
||||
|
||||
``` ~alice
|
||||
$ git pull --rebase
|
||||
$ git log --oneline
|
||||
f6cff86 Third commit by Alice
|
||||
319a7dc Third commit
|
||||
f2de534 Second commit
|
||||
08c788d Initial commit
|
||||
```
|
||||
``` ~alice RAD_SOCKET=/dev/null (stderr)
|
||||
$ git push rad
|
||||
✓ Canonical head updated to f6cff86594495e9beccfeda7c20173e55c1dd9fc
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
f2de534..f6cff86 master -> master
|
||||
```
|
||||
|
||||
One thing of note is that we can revert to an older commit as long as we are
|
||||
still ahead of the other delegates.
|
||||
|
||||
``` ~alice
|
||||
$ git reset --hard HEAD^ -q
|
||||
```
|
||||
``` ~alice RAD_SOCKET=/dev/null (stderr)
|
||||
$ git push -f
|
||||
✓ Canonical head updated to 319a7dc3b195368ded4b099f8c90bbb80addccd3
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
+ f6cff86...319a7dc master -> master (forced update)
|
||||
```
|
||||
@@ -1,60 +0,0 @@
|
||||
In this scenario, we will explore being able to rollback to a previous commit.
|
||||
|
||||
First we add a second delegate, Bob, to our repo. We also change the threshold
|
||||
to 2:
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --title "Add Bob" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --threshold 2 -q
|
||||
069e7d58faa9a7473d27f5510d676af33282796f
|
||||
```
|
||||
|
||||
Bob then syncs these changes and adds a new commit:
|
||||
|
||||
``` ~bob
|
||||
$ rad sync --fetch
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
$ git commit -m "Third commit" --allow-empty -q
|
||||
$ git push rad
|
||||
$ git branch -arv
|
||||
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master f2de534 Second commit
|
||||
rad/master 319a7dc Third commit
|
||||
```
|
||||
|
||||
Alice merges these changes and pushes them, which updates the canonical head:
|
||||
|
||||
``` ~alice
|
||||
$ rad remote add did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob --fetch --no-sync
|
||||
✓ Remote bob added
|
||||
✓ Remote-tracking branch bob/master created for z6Mkt67…v4N1tRk
|
||||
$ git merge bob/master
|
||||
Updating f2de534..319a7dc
|
||||
Fast-forward
|
||||
```
|
||||
|
||||
``` ~alice (stderr)
|
||||
$ git push rad
|
||||
✓ Canonical head updated to 319a7dc3b195368ded4b099f8c90bbb80addccd3
|
||||
✓ Synced with 1 node(s)
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
f2de534..319a7dc master -> master
|
||||
```
|
||||
|
||||
Alice decides that she changes her mind about these changes and rolls back to
|
||||
the previous commit:
|
||||
|
||||
``` ~alice
|
||||
$ git reset --hard f2de534
|
||||
HEAD is now at f2de534 Second commit
|
||||
```
|
||||
|
||||
Since the canonical head is still decidable from this commit she is allowed to
|
||||
push and the new canonical head becomes the previous commit again:
|
||||
|
||||
``` ~alice (stderr)
|
||||
$ git push rad -f
|
||||
✓ Canonical head updated to f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
|
||||
✓ Synced with 1 node(s)
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
+ 319a7dc...f2de534 master -> master (forced update)
|
||||
```
|
||||
@@ -1,90 +0,0 @@
|
||||
```
|
||||
$ git checkout -b alice/1
|
||||
$ git commit -m "Alice's commit" --allow-empty -s
|
||||
[alice/1 87fa120] Alice's commit
|
||||
```
|
||||
|
||||
``` (stderr) RAD_SOCKET=/dev/null
|
||||
$ git push rad HEAD:alice/1
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
* [new branch] HEAD -> alice/1
|
||||
```
|
||||
|
||||
Make sure we can't force-push without `+`:
|
||||
|
||||
``` (stderr)
|
||||
$ git commit --amend -m "Alice's amended commit" --allow-empty -s
|
||||
```
|
||||
``` (stderr) (fail)
|
||||
$ git push rad HEAD:alice/1
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
! [rejected] HEAD -> alice/1 (non-fast-forward)
|
||||
error: failed to push some refs to 'rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi'
|
||||
hint: [..]
|
||||
hint: [..]
|
||||
hint: [..]
|
||||
hint: See the 'Note about fast-forwards' in 'git push --help' for details.
|
||||
```
|
||||
|
||||
And that we can with `+`:
|
||||
|
||||
``` (stderr)
|
||||
$ git push -o no-sync rad +HEAD:alice/1
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
+ 87fa120...145e1e6 HEAD -> alice/1 (forced update)
|
||||
```
|
||||
|
||||
Notice that we used the `-o no-sync` push option to disable syncing after the push.
|
||||
|
||||
```
|
||||
$ git branch -r -vv
|
||||
rad/alice/1 145e1e6 Alice's amended commit
|
||||
rad/master f2de534 Second commit
|
||||
```
|
||||
|
||||
List our namespaced refs:
|
||||
|
||||
```
|
||||
$ git ls-remote rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi 'refs/heads/*'
|
||||
145e1e69bef3ad93d14946ea212249c2fa9b9828 refs/heads/alice/1
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
|
||||
```
|
||||
|
||||
List the canonical refs:
|
||||
|
||||
```
|
||||
$ git ls-remote rad
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
|
||||
```
|
||||
|
||||
```
|
||||
$ rad sync --announce
|
||||
✓ Synced with 1 node(s)
|
||||
```
|
||||
|
||||
Note that it is forbidden to delete the default/canonical branch:
|
||||
|
||||
``` (fail) (stderr)
|
||||
$ git push rad :master
|
||||
error: refusing to delete default branch ref 'refs/heads/master'
|
||||
error: failed to push some refs to 'rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi'
|
||||
```
|
||||
|
||||
If you pass an unsupported push option, you get an error:
|
||||
|
||||
``` (stderr) (fail)
|
||||
$ git push -o alien rad HEAD:alice/2
|
||||
error: unknown push option "alien"
|
||||
```
|
||||
|
||||
We can also push a SHA-1:
|
||||
|
||||
```
|
||||
$ git commit -m "Something good" --allow-empty -s
|
||||
[alice/1 ddcc1f1] Something good
|
||||
```
|
||||
``` (stderr)
|
||||
$ git push -o no-sync rad ddcc1f164eacfd7dba41da9bff3261da3ee79fd3:refs/heads/alice/2
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
* [new branch] ddcc1f164eacfd7dba41da9bff3261da3ee79fd3 -> alice/2
|
||||
```
|
||||
@@ -1,70 +0,0 @@
|
||||
Alice creates an annotated tag and pushed to her `rad` remote:
|
||||
|
||||
``` ~alice
|
||||
$ touch LICENSE
|
||||
$ git add LICENSE
|
||||
$ git commit -am "Add LICENSE"
|
||||
[master 62d19fd] Add LICENSE
|
||||
1 file changed, 0 insertions(+), 0 deletions(-)
|
||||
create mode 100644 LICENSE
|
||||
$ git tag v1.0 -a -m "Release v1.0"
|
||||
```
|
||||
|
||||
``` ~alice (stderr)
|
||||
$ git push rad v1.0 --tags
|
||||
✓ Synced with 1 node(s)
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
* [new tag] v1.0 -> v1.0
|
||||
```
|
||||
|
||||
Bob fetches the tag from Alice, by adding her as a remote:
|
||||
|
||||
``` ~bob
|
||||
$ cd heartwood
|
||||
$ rad remote add z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --name alice
|
||||
✓ Follow policy updated for z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Remote alice added
|
||||
✓ Remote-tracking branch alice/master created for z6MknSL…StBU8Vi
|
||||
```
|
||||
|
||||
Bob is able to fetch Alice's tag into his working copy by using the
|
||||
`--tags` flag:
|
||||
|
||||
``` ~bob (stderr)
|
||||
$ git fetch alice --tags
|
||||
From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
* [new tag] v1.0 -> v1.0
|
||||
```
|
||||
|
||||
Alice forcefully creates a new version of the tag (let's say she made
|
||||
a mistake):
|
||||
|
||||
``` ~alice
|
||||
$ git commit --allow-empty -m "Release: v1.0"
|
||||
[master 8260c04] Release: v1.0
|
||||
$ git tag v1.0 -f -a -m "Release v1.0"
|
||||
Updated tag 'v1.0' (was be18ed6)
|
||||
```
|
||||
|
||||
``` ~alice (stderr)
|
||||
$ git push rad v1.0 -f
|
||||
✓ Synced with 1 node(s)
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
+ be18ed6...9dbdebc v1.0 -> v1.0 (forced update)
|
||||
```
|
||||
|
||||
We ensure that Bob is still able to fetch from Alice and get the new
|
||||
update of the tag:
|
||||
|
||||
``` ~bob
|
||||
$ rad sync -f
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
|
||||
``` ~bob (stderr)
|
||||
$ git fetch alice --tags -f
|
||||
From rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
t [tag update] v1.0 -> v1.0
|
||||
```
|
||||
@@ -1,17 +0,0 @@
|
||||
Note that aliases must not be longer than 32 bytes, or you will get an error.
|
||||
There are other rules as well:
|
||||
|
||||
``` (fail)
|
||||
$ rad auth --alias "5fad63fe6b339fa92c588d926121bea6240773a7"
|
||||
✗ Error: rad auth: alias cannot be greater than 32 bytes
|
||||
```
|
||||
|
||||
``` (fail)
|
||||
$ rad auth --alias "john doe"
|
||||
✗ Error: rad auth: alias cannot contain whitespace or control characters
|
||||
```
|
||||
|
||||
``` (fail)
|
||||
$ rad auth --alias ""
|
||||
✗ Error: rad auth: alias cannot be empty
|
||||
```
|
||||
@@ -1,32 +0,0 @@
|
||||
Initializing a new identity with `rad-auth`.
|
||||
The example below is run with `RAD_PASSPHRASE` set.
|
||||
|
||||
```
|
||||
$ rad auth --alias "alice"
|
||||
|
||||
Initializing your radicle 👾 identity
|
||||
|
||||
✓ Creating your Ed25519 keypair...
|
||||
✓ Your Radicle DID is did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi. This identifies your device. Run `rad self` to show it at all times.
|
||||
✓ You're all set.
|
||||
|
||||
✗ Hint: install ssh-agent to have it fill in your passphrase for you when signing.
|
||||
|
||||
To create a Radicle repository, run `rad init` from a Git repository with at least one commit.
|
||||
To clone a repository, run `rad clone <rid>`. For example, `rad clone rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5` clones the Radicle 'heartwood' repository.
|
||||
To get a list of all commands, run `rad`.
|
||||
```
|
||||
|
||||
You can get the above information at all times using the `self` command:
|
||||
|
||||
```
|
||||
$ rad self --did
|
||||
did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
```
|
||||
|
||||
You can also show your alias:
|
||||
|
||||
```
|
||||
$ rad self --alias
|
||||
alice
|
||||
```
|
||||
@@ -1,55 +0,0 @@
|
||||
When using an open seeding policy, it can be useful to block individual
|
||||
repositories from being seeded.
|
||||
|
||||
For instance, if our default policy is to seed, any unknown repository will
|
||||
have its policy set to allow seeding:
|
||||
```
|
||||
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --policy
|
||||
Repository rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji is being seeded with scope `all`
|
||||
```
|
||||
|
||||
Since there is no policy specific to this repository, there's nothing to be
|
||||
removed.
|
||||
|
||||
```
|
||||
$ rad seed
|
||||
No seeding policies to show.
|
||||
```
|
||||
|
||||
But if we wanted to prevent this repository from being seeded, while
|
||||
allowing all other repositories, we could use `rad block`:
|
||||
|
||||
```
|
||||
$ rad block rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Policy for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji set to 'block'
|
||||
```
|
||||
|
||||
We can see that it is now no longer seeded:
|
||||
|
||||
```
|
||||
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --policy
|
||||
Repository rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji is not being seeded
|
||||
```
|
||||
|
||||
And a 'block' policy was added:
|
||||
|
||||
```
|
||||
$ rad seed
|
||||
╭───────────────────────────────────────────────────────────╮
|
||||
│ Repository Name Policy Scope │
|
||||
├───────────────────────────────────────────────────────────┤
|
||||
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji block all │
|
||||
╰───────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
If we want to reverse the blocking of the RID we can use `rad unblock`:
|
||||
|
||||
```
|
||||
$ rad unblock rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ The 'block' policy for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji is removed
|
||||
```
|
||||
|
||||
```
|
||||
$ rad seed
|
||||
No seeding policies to show.
|
||||
```
|
||||
@@ -1,23 +0,0 @@
|
||||
```
|
||||
$ cd heartwood
|
||||
```
|
||||
|
||||
View the repository configuration:
|
||||
|
||||
```
|
||||
$ cat .git/config
|
||||
[core]
|
||||
bare = false
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
logallrefupdates = true
|
||||
[push]
|
||||
default = upstream
|
||||
[remote "rad"]
|
||||
url = rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
fetch = +refs/heads/*:refs/remotes/rad/*
|
||||
pushurl = rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
[branch "master"]
|
||||
remote = rad
|
||||
merge = refs/heads/master
|
||||
```
|
||||
@@ -1,25 +0,0 @@
|
||||
```
|
||||
$ cd heartwood
|
||||
```
|
||||
|
||||
View the repository configuration:
|
||||
|
||||
```
|
||||
$ cat .git/config
|
||||
[core]
|
||||
bare = false
|
||||
repositoryformatversion = 0
|
||||
filemode = true
|
||||
ignorecase = true
|
||||
precomposeunicode = true
|
||||
logallrefupdates = true
|
||||
[push]
|
||||
default = upstream
|
||||
[remote "rad"]
|
||||
url = rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
fetch = +refs/heads/*:refs/remotes/rad/*
|
||||
pushurl = rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
[branch "master"]
|
||||
remote = rad
|
||||
merge = refs/heads/master
|
||||
```
|
||||
@@ -1,53 +0,0 @@
|
||||
With the `rad checkout` command, you can create a new working copy from an
|
||||
existing project.
|
||||
|
||||
```
|
||||
$ rad checkout rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Repository checkout successful under ./heartwood
|
||||
```
|
||||
|
||||
Let's have a look at what the command did. Navigate to the working copy:
|
||||
|
||||
```
|
||||
$ cd heartwood
|
||||
```
|
||||
|
||||
Check the README:
|
||||
```
|
||||
$ cat README
|
||||
Hello World!
|
||||
```
|
||||
|
||||
Check the repository status:
|
||||
|
||||
```
|
||||
$ git status
|
||||
On branch master
|
||||
Your branch is up to date with 'rad/master'.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
```
|
||||
|
||||
Check the remote configuration:
|
||||
|
||||
```
|
||||
$ git remote --verbose
|
||||
rad rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji (fetch)
|
||||
rad rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (push)
|
||||
```
|
||||
|
||||
List the branches:
|
||||
|
||||
```
|
||||
$ git branch --all
|
||||
* master
|
||||
remotes/rad/master
|
||||
```
|
||||
|
||||
List the references:
|
||||
|
||||
```
|
||||
$ git show-ref
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/heads/master
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354 refs/remotes/rad/master
|
||||
```
|
||||
@@ -1,67 +0,0 @@
|
||||
We cannot delete a repository, since that can cause data integrity
|
||||
issues. However, we can clean the storage of remotes that are not the
|
||||
local peer or the repository delegates. To do this we can use the `rad
|
||||
clean` command.
|
||||
|
||||
First let's look at what we have locally:
|
||||
|
||||
``` ~alice
|
||||
$ rad ls
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Name RID Visibility Head Description │
|
||||
├───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ heartwood rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji public f2de534 Radicle Heartwood Protocol & Stack │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Let's also inspect what remotes are in the repository:
|
||||
|
||||
``` ~alice
|
||||
$ rad inspect --sigrefs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi f209c9f68aa689af24220a20462e13ee9dfb2a95
|
||||
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk 161b775a3509c8098de67f57f750972bba015b31
|
||||
```
|
||||
|
||||
Now let's clean the `heartwood` project:
|
||||
|
||||
``` ~alice
|
||||
$ rad clean rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --no-confirm
|
||||
Removed z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
✓ Successfully cleaned rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
```
|
||||
|
||||
Inspecting the remotes again, we see that Bob is now gone:
|
||||
|
||||
``` ~alice
|
||||
$ rad inspect --sigrefs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi f209c9f68aa689af24220a20462e13ee9dfb2a95
|
||||
```
|
||||
|
||||
Note that Bob will be fetched again if we do not untrack his
|
||||
node. Currently, there is no per repository tracking so it's not
|
||||
possible to stop fetching Bob for this particular repository.
|
||||
|
||||
Cleaning a repository again will remove no remotes, since we're
|
||||
already at the minimal set of remotes:
|
||||
|
||||
``` ~alice
|
||||
$ rad clean rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --no-confirm
|
||||
✓ Successfully cleaned rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
```
|
||||
|
||||
Since Eve did not fork the repository, and has no refs of their own,
|
||||
when they run `rad clean` it will remove the project entirely:
|
||||
|
||||
``` ~eve
|
||||
$ rad clean rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --no-confirm
|
||||
Removed z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
✓ Successfully cleaned rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
```
|
||||
|
||||
And attempting to clean the repository again, or any non-existent
|
||||
repository, has no effect on the storage at all:
|
||||
|
||||
``` ~eve (fail)
|
||||
$ rad clean rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --no-confirm
|
||||
✗ Error: repository rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji was not found
|
||||
```
|
||||
@@ -1,91 +0,0 @@
|
||||
```
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope all
|
||||
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'all'
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Creating checkout in ./heartwood..
|
||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Repository successfully cloned under [..]/heartwood/
|
||||
╭────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
│ Radicle Heartwood Protocol & Stack │
|
||||
│ 0 issues · 0 patches │
|
||||
╰────────────────────────────────────╯
|
||||
Run `cd ./heartwood` to go to the repository directory.
|
||||
```
|
||||
|
||||
We can now have a look at the new working copy that was created from the cloned
|
||||
repository:
|
||||
|
||||
```
|
||||
$ cd heartwood
|
||||
$ cat README
|
||||
Hello World!
|
||||
```
|
||||
|
||||
Let's check that we have Bob and Alice's namespaces in storage:
|
||||
|
||||
```
|
||||
$ rad inspect --refs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
└── refs
|
||||
├── cobs
|
||||
│ └── xyz.radicle.id
|
||||
│ └── [...]
|
||||
├── heads
|
||||
│ └── master
|
||||
└── rad
|
||||
├── id
|
||||
└── sigrefs
|
||||
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
└── refs
|
||||
├── heads
|
||||
│ └── master
|
||||
└── rad
|
||||
└── sigrefs
|
||||
```
|
||||
|
||||
We can then setup a git remote for `bob`:
|
||||
|
||||
```
|
||||
$ rad remote add z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --name bob --no-sync --fetch
|
||||
✓ Remote bob added
|
||||
✓ Remote-tracking branch bob/master created for z6Mkt67…v4N1tRk
|
||||
$ git branch --remotes
|
||||
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master
|
||||
bob/master
|
||||
rad/master
|
||||
```
|
||||
|
||||
We can also create our own fork just by pushing:
|
||||
|
||||
``` (stderr)
|
||||
$ git push -o no-sync rad master
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z
|
||||
* [new branch] master -> master
|
||||
```
|
||||
```
|
||||
$ rad inspect --refs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
└── refs
|
||||
├── cobs
|
||||
│ └── xyz.radicle.id
|
||||
│ └── [...]
|
||||
├── heads
|
||||
│ └── master
|
||||
└── rad
|
||||
├── id
|
||||
└── sigrefs
|
||||
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
└── refs
|
||||
├── heads
|
||||
│ └── master
|
||||
└── rad
|
||||
└── sigrefs
|
||||
z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z
|
||||
└── refs
|
||||
├── heads
|
||||
│ └── master
|
||||
└── rad
|
||||
└── sigrefs
|
||||
```
|
||||
@@ -1,21 +0,0 @@
|
||||
If we're not connecting to seed nodes when cloning, the `clone` command will
|
||||
automatically connect to the necessary seeds.
|
||||
|
||||
```
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'all'
|
||||
✓ Connecting to z6Mkt67…v4N1tRk@[..]
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk..
|
||||
✓ Connecting to z6MknSL…StBU8Vi@[..]
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Creating checkout in ./heartwood..
|
||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Repository successfully cloned under [..]/heartwood/
|
||||
╭────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
│ Radicle Heartwood Protocol & Stack │
|
||||
│ 0 issues · 0 patches │
|
||||
╰────────────────────────────────────╯
|
||||
Run `cd ./heartwood` to go to the repository directory.
|
||||
```
|
||||
@@ -1,27 +0,0 @@
|
||||
We can specify where a repository gets cloned into on our filesystem
|
||||
by specifying the directory in the `rad clone` command:
|
||||
|
||||
```
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed Developer/Radicle
|
||||
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'followed'
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Creating checkout in ./Developer/Radicle..
|
||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Repository successfully cloned under [..]/Developer/Radicle/
|
||||
╭────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
│ Radicle Heartwood Protocol & Stack │
|
||||
│ 0 issues · 0 patches │
|
||||
╰────────────────────────────────────╯
|
||||
Run `cd [..]/Developer/Radicle` to go to the repository directory.
|
||||
```
|
||||
|
||||
Note that attempting to clone into a directory that already exists,
|
||||
and is not empty, will fail:
|
||||
|
||||
``` (fail)
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed Developer/Radicle
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✗ Error: the directory path "Developer/Radicle" already exists
|
||||
```
|
||||
@@ -1,32 +0,0 @@
|
||||
Eve knows about three seeds.
|
||||
|
||||
```
|
||||
$ rad node routing
|
||||
╭─────────────────────────────────────────────────────╮
|
||||
│ RID NID │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji z6MknSL…StBU8Vi │
|
||||
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji z6MksFq…bS9wzpT │
|
||||
│ rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji z6Mkt67…v4N1tRk │
|
||||
╰─────────────────────────────────────────────────────╯
|
||||
```
|
||||
When she tries to clone, one of those will fail to fetch. But the clone command
|
||||
still returns successfully.
|
||||
|
||||
```
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --timeout 3
|
||||
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'all'
|
||||
✗ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk.. error: failed to perform fetch handshake
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✗ Connecting to z6MksFq…bS9wzpT@[..].. error: connection reset
|
||||
✓ Creating checkout in ./heartwood..
|
||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Repository successfully cloned under [..]/heartwood/
|
||||
╭────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
│ Radicle Heartwood Protocol & Stack │
|
||||
│ 0 issues · 0 patches │
|
||||
╰────────────────────────────────────╯
|
||||
Run `cd ./heartwood` to go to the repository directory.
|
||||
```
|
||||
@@ -1,7 +0,0 @@
|
||||
Trying to clone a repository that is not in our routing table returns an error:
|
||||
|
||||
``` (fail)
|
||||
$ rad clone rad:zVNuptPuk5XauitpCWSNVCXGGfXW --scope followed
|
||||
✓ Seeding policy updated for rad:zVNuptPuk5XauitpCWSNVCXGGfXW with scope 'followed'
|
||||
✗ Error: no seeds found for rad:zVNuptPuk5XauitpCWSNVCXGGfXW
|
||||
```
|
||||
@@ -1,74 +0,0 @@
|
||||
To create a local copy of a repository on the radicle network, we use the
|
||||
`clone` command, followed by the identifier or *RID* of the repository:
|
||||
|
||||
```
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed
|
||||
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'followed'
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Creating checkout in ./heartwood..
|
||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Repository successfully cloned under [..]/heartwood/
|
||||
╭────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
│ Radicle Heartwood Protocol & Stack │
|
||||
│ 0 issues · 0 patches │
|
||||
╰────────────────────────────────────╯
|
||||
Run `cd ./heartwood` to go to the repository directory.
|
||||
```
|
||||
|
||||
We can now have a look at the new working copy that was created from the cloned
|
||||
repository:
|
||||
|
||||
```
|
||||
$ cd heartwood
|
||||
$ ls
|
||||
README
|
||||
$ cat README
|
||||
Hello World!
|
||||
$ git status
|
||||
On branch master
|
||||
Your branch is up to date with 'rad/master'.
|
||||
|
||||
nothing to commit, working tree clean
|
||||
```
|
||||
|
||||
Let's check that the remote tracking branch was setup correctly:
|
||||
|
||||
```
|
||||
$ git branch --remotes
|
||||
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master
|
||||
rad/master
|
||||
```
|
||||
|
||||
The first branch is ours, and the second points to the repository delegate.
|
||||
We can also take a look at the remotes:
|
||||
|
||||
```
|
||||
$ git remote -v
|
||||
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (fetch)
|
||||
alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (push)
|
||||
rad rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji (fetch)
|
||||
rad rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (push)
|
||||
```
|
||||
|
||||
Let's check the last commit!
|
||||
|
||||
```
|
||||
$ git log -n 1
|
||||
commit f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
|
||||
Author: anonymous <anonymous@radicle.xyz>
|
||||
Date: Mon Jan 1 14:39:16 2018 +0000
|
||||
|
||||
Second commit
|
||||
```
|
||||
|
||||
Cloned repositories show up in `rad ls`:
|
||||
```
|
||||
$ rad ls --seeded
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Name RID Visibility Head Description │
|
||||
├───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ heartwood rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji public f2de534 Radicle Heartwood Protocol & Stack │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
@@ -1,141 +0,0 @@
|
||||
Handle arbitrary COBs.
|
||||
|
||||
First create an issue.
|
||||
|
||||
```
|
||||
$ rad issue open --title "flux capacitor underpowered" --description "Flux capacitor power requirements exceed current supply" --no-announce
|
||||
╭─────────────────────────────────────────────────────────╮
|
||||
│ Title flux capacitor underpowered │
|
||||
│ Issue d87dcfe8c2b3200e78b128d9b959cfdf7063fefe │
|
||||
│ Author alice (you) │
|
||||
│ Status open │
|
||||
│ │
|
||||
│ Flux capacitor power requirements exceed current supply │
|
||||
╰─────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
The issue is now listed under our project.
|
||||
|
||||
```
|
||||
$ rad issue list
|
||||
╭──────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Labels Assignees Opened │
|
||||
├──────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● d87dcfe flux capacitor underpowered alice (you) now │
|
||||
╰──────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Let's create a patch, too.
|
||||
|
||||
```
|
||||
$ git checkout -b flux-capacitor-power
|
||||
$ touch REQUIREMENTS
|
||||
$ git add REQUIREMENTS
|
||||
$ git commit -v -m "Define power requirements"
|
||||
[flux-capacitor-power 3e674d1] Define power requirements
|
||||
1 file changed, 0 insertions(+), 0 deletions(-)
|
||||
create mode 100644 REQUIREMENTS
|
||||
$ git push rad -o patch.message="Define power requirements" -o patch.message="See details." HEAD:refs/patches
|
||||
```
|
||||
|
||||
Patch can be listed.
|
||||
|
||||
```
|
||||
$ rad patch
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Reviews Head + - Updated │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● aa45913 Define power requirements alice (you) - 3e674d1 +0 -0 now │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Both issue and patch COBs can be listed.
|
||||
|
||||
```
|
||||
$ rad cob list --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.issue
|
||||
d87dcfe8c2b3200e78b128d9b959cfdf7063fefe
|
||||
$ rad cob list --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.patch
|
||||
aa45913e757cacd46972733bddee5472c78fa32a
|
||||
```
|
||||
|
||||
We can look at the issue COB.
|
||||
|
||||
```
|
||||
$ rad cob log --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.issue --object d87dcfe8c2b3200e78b128d9b959cfdf7063fefe
|
||||
commit d87dcfe8c2b3200e78b128d9b959cfdf7063fefe
|
||||
resource 0656c217f917c3e06234771e9ecae53aba5e173e
|
||||
author z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
date Thu, 15 Dec 2022 17:28:04 +0000
|
||||
|
||||
{
|
||||
"body": "Flux capacitor power requirements exceed current supply",
|
||||
"type": "comment"
|
||||
}
|
||||
|
||||
{
|
||||
"title": "flux capacitor underpowered",
|
||||
"type": "edit"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
We can look at the patch COB too.
|
||||
|
||||
```
|
||||
$ rad cob log --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.patch --object aa45913e757cacd46972733bddee5472c78fa32a
|
||||
commit aa45913e757cacd46972733bddee5472c78fa32a
|
||||
resource 0656c217f917c3e06234771e9ecae53aba5e173e
|
||||
rel 3e674d1a1df90807e934f9ae5da2591dd6848a33
|
||||
rel f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
|
||||
author z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
date Thu, 15 Dec 2022 17:28:04 +0000
|
||||
|
||||
{
|
||||
"base": "f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354",
|
||||
"description": "See details.",
|
||||
"oid": "3e674d1a1df90807e934f9ae5da2591dd6848a33",
|
||||
"type": "revision"
|
||||
}
|
||||
|
||||
{
|
||||
"target": "delegates",
|
||||
"title": "Define power requirements",
|
||||
"type": "edit"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Finally let's updated the issue and see the `parent` header:
|
||||
|
||||
```
|
||||
$ rad issue label d87dcfe --add bug --no-announce
|
||||
$ rad cob log --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.issue --object d87dcfe8c2b3200e78b128d9b959cfdf7063fefe
|
||||
commit abec0a9f3c945594c4e78d24d8ec679e56b22b79
|
||||
resource 0656c217f917c3e06234771e9ecae53aba5e173e
|
||||
parent d87dcfe8c2b3200e78b128d9b959cfdf7063fefe
|
||||
author z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
date Thu, 15 Dec 2022 17:28:04 +0000
|
||||
|
||||
{
|
||||
"labels": [
|
||||
"bug"
|
||||
],
|
||||
"type": "label"
|
||||
}
|
||||
|
||||
commit d87dcfe8c2b3200e78b128d9b959cfdf7063fefe
|
||||
resource 0656c217f917c3e06234771e9ecae53aba5e173e
|
||||
author z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
date Thu, 15 Dec 2022 17:28:04 +0000
|
||||
|
||||
{
|
||||
"body": "Flux capacitor power requirements exceed current supply",
|
||||
"type": "comment"
|
||||
}
|
||||
|
||||
{
|
||||
"title": "flux capacitor underpowered",
|
||||
"type": "edit"
|
||||
}
|
||||
|
||||
```
|
||||
@@ -1,184 +0,0 @@
|
||||
Well known COBs, for example issues and patches, can not only be showed via porcelain commands such as
|
||||
`rad issue show` and `rad patch show`, but also using the plumbing command `rad cob show`.
|
||||
While humans likely prefer to use `rad issue show` and `rad patch show`, this command makes integration
|
||||
with other software components easier.
|
||||
|
||||
First create an issue.
|
||||
|
||||
```
|
||||
$ rad issue open --title "spice harvester broken" --description "Fremen have attacked, maybe we went too far?" --no-announce
|
||||
╭──────────────────────────────────────────────────╮
|
||||
│ Title spice harvester broken │
|
||||
│ Issue 9de644864342d7a505eb8d58d1ef20e5bb05de2e │
|
||||
│ Author alice (you) │
|
||||
│ Status open │
|
||||
│ │
|
||||
│ Fremen have attacked, maybe we went too far? │
|
||||
╰──────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
The issue is now listed under our project.
|
||||
|
||||
```
|
||||
$ rad issue list
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Labels Assignees Opened │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● 9de6448 spice harvester broken alice (you) now │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Let's create a patch, too.
|
||||
|
||||
```
|
||||
$ git checkout -b spice-harvester-broken
|
||||
$ touch TREATY.md
|
||||
$ git add TREATY.md
|
||||
$ git commit -v -m "Start drafting peace treaty"
|
||||
[spice-harvester-broken 575ed68] Start drafting peace treaty
|
||||
1 file changed, 0 insertions(+), 0 deletions(-)
|
||||
create mode 100644 TREATY.md
|
||||
$ git push rad -o patch.message="Start drafting peace treaty" -o patch.message="See details." HEAD:refs/patches
|
||||
```
|
||||
|
||||
Patch can be listed.
|
||||
|
||||
```
|
||||
$ rad patch
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Reviews Head + - Updated │
|
||||
├───────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● d1f7f86 Start drafting peace treaty alice (you) - 575ed68 +0 -0 now │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Both issue and patch COBs can be listed.
|
||||
|
||||
```
|
||||
$ rad cob list --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.issue
|
||||
9de644864342d7a505eb8d58d1ef20e5bb05de2e
|
||||
$ rad cob list --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.patch
|
||||
d1f7f869fde9fac19c1779c4c2e77e8361333f91
|
||||
```
|
||||
|
||||
We can show the issue COB.
|
||||
|
||||
```
|
||||
$ rad cob show --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.issue --object 9de644864342d7a505eb8d58d1ef20e5bb05de2e
|
||||
{
|
||||
"assignees": [],
|
||||
"title": "spice harvester broken",
|
||||
"state": {
|
||||
"status": "open"
|
||||
},
|
||||
"labels": [],
|
||||
"thread": {
|
||||
"comments": {
|
||||
"9de644864342d7a505eb8d58d1ef20e5bb05de2e": {
|
||||
"author": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"reactions": [],
|
||||
"resolved": false,
|
||||
"body": "Fremen have attacked, maybe we went too far?",
|
||||
"edits": [
|
||||
{
|
||||
"author": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"timestamp": 1671125284000,
|
||||
"body": "Fremen have attacked, maybe we went too far?",
|
||||
"embeds": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
"9de644864342d7a505eb8d58d1ef20e5bb05de2e"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We can show the patch COB too.
|
||||
|
||||
```
|
||||
$ rad cob show --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.patch --object d1f7f869fde9fac19c1779c4c2e77e8361333f91
|
||||
{
|
||||
"title": "Start drafting peace treaty",
|
||||
"author": {
|
||||
"id": "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
},
|
||||
"state": {
|
||||
"status": "open"
|
||||
},
|
||||
"target": "delegates",
|
||||
"labels": [],
|
||||
"merges": {},
|
||||
"revisions": {
|
||||
"d1f7f869fde9fac19c1779c4c2e77e8361333f91": {
|
||||
"id": "d1f7f869fde9fac19c1779c4c2e77e8361333f91",
|
||||
"author": {
|
||||
"id": "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
},
|
||||
"description": [
|
||||
{
|
||||
"author": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"timestamp": 1671125284000,
|
||||
"body": "See details.",
|
||||
"embeds": []
|
||||
}
|
||||
],
|
||||
"base": "f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354",
|
||||
"oid": "575ed68c716d6aae81ea6b718fd9ac66a8eae532",
|
||||
"discussion": {
|
||||
"comments": {},
|
||||
"timeline": []
|
||||
},
|
||||
"reviews": {},
|
||||
"timestamp": 1671125284000,
|
||||
"resolves": [],
|
||||
"reactions": []
|
||||
}
|
||||
},
|
||||
"assignees": [],
|
||||
"timeline": [
|
||||
"d1f7f869fde9fac19c1779c4c2e77e8361333f91"
|
||||
],
|
||||
"reviews": {}
|
||||
}
|
||||
```
|
||||
|
||||
Finally let's update the issue and see the output of `rad cob show` also changes.
|
||||
|
||||
```
|
||||
$ rad issue label 9de6448 --add bug --no-announce
|
||||
$ rad cob show --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --type xyz.radicle.issue --object 9de644864342d7a505eb8d58d1ef20e5bb05de2e
|
||||
{
|
||||
"assignees": [],
|
||||
"title": "spice harvester broken",
|
||||
"state": {
|
||||
"status": "open"
|
||||
},
|
||||
"labels": [
|
||||
"bug"
|
||||
],
|
||||
"thread": {
|
||||
"comments": {
|
||||
"9de644864342d7a505eb8d58d1ef20e5bb05de2e": {
|
||||
"author": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"reactions": [],
|
||||
"resolved": false,
|
||||
"body": "Fremen have attacked, maybe we went too far?",
|
||||
"edits": [
|
||||
{
|
||||
"author": "z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"timestamp": 1671125284000,
|
||||
"body": "Fremen have attacked, maybe we went too far?",
|
||||
"embeds": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"timeline": [
|
||||
"9de644864342d7a505eb8d58d1ef20e5bb05de2e"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -1,70 +0,0 @@
|
||||
The `rad config` command is used to manage the local user configuration.
|
||||
In its simplest form, `rad config` prints the current configuration.
|
||||
|
||||
```
|
||||
$ rad config
|
||||
{
|
||||
"publicExplorer": "https://app.radicle.xyz/nodes/$host/$rid$path",
|
||||
"preferredSeeds": [
|
||||
"z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776",
|
||||
"z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz@seed.radicle.xyz:8776"
|
||||
],
|
||||
"web": {
|
||||
"pinned": {
|
||||
"repositories": []
|
||||
}
|
||||
},
|
||||
"cli": {
|
||||
"hints": true
|
||||
},
|
||||
"node": {
|
||||
"alias": "alice",
|
||||
"listen": [],
|
||||
"peers": {
|
||||
"type": "dynamic"
|
||||
},
|
||||
"connect": [],
|
||||
"externalAddresses": [],
|
||||
"network": "main",
|
||||
"log": "INFO",
|
||||
"relay": "auto",
|
||||
"limits": {
|
||||
"routingMaxSize": 1000,
|
||||
"routingMaxAge": 604800,
|
||||
"gossipMaxAge": 1209600,
|
||||
"fetchConcurrency": 1,
|
||||
"maxOpenFiles": 4096,
|
||||
"rate": {
|
||||
"inbound": {
|
||||
"fillRate": 5.0,
|
||||
"capacity": 1024
|
||||
},
|
||||
"outbound": {
|
||||
"fillRate": 10.0,
|
||||
"capacity": 2048
|
||||
}
|
||||
},
|
||||
"connection": {
|
||||
"inbound": 128,
|
||||
"outbound": 16
|
||||
}
|
||||
},
|
||||
"workers": 8,
|
||||
"seedingPolicy": {
|
||||
"default": "block"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also get any value in the configuration by path, eg.
|
||||
|
||||
```
|
||||
$ rad config get node.alias
|
||||
alice
|
||||
$ rad config get preferredSeeds
|
||||
z6MkrLMMsiPWUcNPHcRajuMi9mDfYckSoJyPwwnknocNYPm7@seed.radicle.garden:8776
|
||||
z6MksmpU5b1dS7oaqF2bHXhQi1DWy2hB7Mh9CuN7y1DN6QSz@seed.radicle.xyz:8776
|
||||
$ rad config get node.limits.routingMaxSize
|
||||
1000
|
||||
```
|
||||
@@ -1,159 +0,0 @@
|
||||
Exploring `rad diff`.
|
||||
|
||||
``` ./main.c
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void) {
|
||||
printf("Hello World!\n");
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
```
|
||||
$ ls
|
||||
README
|
||||
main.c
|
||||
```
|
||||
|
||||
```
|
||||
$ git mv README README.md
|
||||
$ git add main.c
|
||||
$ git commit -m "Make changes"
|
||||
[master 5f771e0] Make changes
|
||||
2 files changed, 6 insertions(+)
|
||||
rename README => README.md (100%)
|
||||
create mode 100644 main.c
|
||||
```
|
||||
|
||||
```
|
||||
$ rad diff HEAD^ HEAD
|
||||
╭────────────────────────────────────────────╮
|
||||
│ README -> README.md ❲moved❳ │
|
||||
╰────────────────────────────────────────────╯
|
||||
|
||||
╭────────────────────────────────────────────╮
|
||||
│ main.c +6 ❲created❳ │
|
||||
├────────────────────────────────────────────┤
|
||||
│ @@ -0,0 +1,6 @@ │
|
||||
│ 1 + #include <stdio.h> │
|
||||
│ 2 + │
|
||||
│ 3 + int main(void) { │
|
||||
│ 4 + printf("Hello World!/n"); │
|
||||
│ 5 + return 0; │
|
||||
│ 6 + } │
|
||||
╰────────────────────────────────────────────╯
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
$ sed -i 's/Hello World/Hello Radicle/' main.c
|
||||
$ rad diff
|
||||
╭──────────────────────────────────────────────╮
|
||||
│ main.c -1 +1 │
|
||||
├──────────────────────────────────────────────┤
|
||||
│ @@ -1,6 +1,6 @@ │
|
||||
│ 1 1 #include <stdio.h> │
|
||||
│ 2 2 │
|
||||
│ 3 3 int main(void) { │
|
||||
│ 4 - printf("Hello World!/n"); │
|
||||
│ 4 + printf("Hello Radicle!/n"); │
|
||||
│ 5 5 return 0; │
|
||||
│ 6 6 } │
|
||||
╰──────────────────────────────────────────────╯
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
$ git add main.c
|
||||
$ rad diff
|
||||
$ rad diff --staged
|
||||
╭──────────────────────────────────────────────╮
|
||||
│ main.c -1 +1 │
|
||||
├──────────────────────────────────────────────┤
|
||||
│ @@ -1,6 +1,6 @@ │
|
||||
│ 1 1 #include <stdio.h> │
|
||||
│ 2 2 │
|
||||
│ 3 3 int main(void) { │
|
||||
│ 4 - printf("Hello World!/n"); │
|
||||
│ 4 + printf("Hello Radicle!/n"); │
|
||||
│ 5 5 return 0; │
|
||||
│ 6 6 } │
|
||||
╰──────────────────────────────────────────────╯
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
$ git rm -f -q main.c
|
||||
$ rad diff --staged
|
||||
╭────────────────────────────────────────────╮
|
||||
│ main.c -6 ❲deleted❳ │
|
||||
├────────────────────────────────────────────┤
|
||||
│ @@ -1,6 +0,0 @@ │
|
||||
│ 1 - #include <stdio.h> │
|
||||
│ 2 - │
|
||||
│ 3 - int main(void) { │
|
||||
│ 4 - printf("Hello World!/n"); │
|
||||
│ 5 - return 0; │
|
||||
│ 6 - } │
|
||||
╰────────────────────────────────────────────╯
|
||||
|
||||
```
|
||||
|
||||
For now, copies are not detected.
|
||||
|
||||
```
|
||||
$ git reset --hard master -q
|
||||
$ mkdir docs
|
||||
$ cp README.md docs/README.md
|
||||
$ git add docs
|
||||
$ rad diff --staged
|
||||
╭─────────────────────────────╮
|
||||
│ docs/README.md +1 ❲created❳ │
|
||||
├─────────────────────────────┤
|
||||
│ @@ -0,0 +1,1 @@ │
|
||||
│ 1 + Hello World! │
|
||||
╰─────────────────────────────╯
|
||||
|
||||
$ git reset
|
||||
$ git checkout .
|
||||
```
|
||||
|
||||
Empty file.
|
||||
|
||||
```
|
||||
$ touch EMPTY
|
||||
$ git add EMPTY
|
||||
$ rad diff --staged
|
||||
╭─────────────────╮
|
||||
│ EMPTY ❲created❳ │
|
||||
╰─────────────────╯
|
||||
|
||||
$ git reset
|
||||
$ git checkout .
|
||||
```
|
||||
|
||||
File mode change.
|
||||
|
||||
```
|
||||
$ chmod +x README.md
|
||||
$ rad diff
|
||||
╭───────────────────────────────────────────╮
|
||||
│ README.md 100644 -> 100755 ❲mode changed❳ │
|
||||
╰───────────────────────────────────────────╯
|
||||
|
||||
$ git reset -q
|
||||
$ git checkout .
|
||||
```
|
||||
|
||||
Binary file.
|
||||
|
||||
```
|
||||
$ touch file.bin
|
||||
$ truncate -s 8 file.bin
|
||||
$ git add file.bin
|
||||
$ rad diff --staged
|
||||
╭─────────────────────────────╮
|
||||
│ file.bin ❲binary❳ ❲created❳ │
|
||||
╰─────────────────────────────╯
|
||||
|
||||
```
|
||||
@@ -1,27 +0,0 @@
|
||||
Using `rad clone` is useful if we want to create and fetch a project
|
||||
that exists on Radicle, but perhaps we're in a scenario where we may
|
||||
already have an existing Git repository and so a full clone is not
|
||||
necessary.
|
||||
|
||||
Instead, we want to fetch the project from the network into our local
|
||||
storage. In this scenario, we know that the project is
|
||||
`rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji`. In order to fetch it, we first
|
||||
have to update our seeding policy for the project.
|
||||
|
||||
```
|
||||
$ rad seed rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --no-fetch
|
||||
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'all'
|
||||
```
|
||||
|
||||
Now that the project is seeding we can fetch it and we will have it in
|
||||
our local storage. Note that the `seed` command can also be told to fetch
|
||||
by passing the `--fetch` option.
|
||||
|
||||
```
|
||||
$ rad sync --fetch rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
|
||||
However, we don't have a local fork of the project. We can follow this
|
||||
up with [rad-fork](rad-fork.md).
|
||||
@@ -1,52 +0,0 @@
|
||||
If we have fetched a project, then we do not have a fork of the
|
||||
repository in the storage, i.e. there is no ref hierarchy for our
|
||||
NID. This is demonstrated below where our NID is
|
||||
`z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk`:
|
||||
|
||||
```
|
||||
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --refs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
└── refs
|
||||
├── cobs
|
||||
│ └── xyz.radicle.id
|
||||
│ └── [...]
|
||||
├── heads
|
||||
│ └── master
|
||||
└── rad
|
||||
├── id
|
||||
└── sigrefs
|
||||
```
|
||||
|
||||
To remedy this, we can use the `rad fork` command for the project we
|
||||
wish to fork:
|
||||
|
||||
```
|
||||
$ rad fork rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Forked repository rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
```
|
||||
|
||||
Now, if we `rad inspect` the project's refs again we will see that we
|
||||
have a copy of the main set of refs:
|
||||
|
||||
```
|
||||
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --refs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
└── refs
|
||||
├── cobs
|
||||
│ └── xyz.radicle.id
|
||||
│ └── [...]
|
||||
├── heads
|
||||
│ └── master
|
||||
└── rad
|
||||
├── id
|
||||
└── sigrefs
|
||||
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
└── refs
|
||||
├── heads
|
||||
│ └── master
|
||||
└── rad
|
||||
└── sigrefs
|
||||
```
|
||||
|
||||
We are now able to setup a remote in our own working copy of the
|
||||
project and push to our own fork.
|
||||
@@ -1,189 +0,0 @@
|
||||
It's common that in projects where there are already multiple
|
||||
delegates that one of those delegates meets someone that they want to
|
||||
bring into the project. So let's see how Alice and Bob end up inviting
|
||||
Eve to the project.
|
||||
|
||||
First, we'll start off with Alice adding Bob. It's necessary for Bob
|
||||
to have a fork of the project and Alice must be aware of the fork:
|
||||
|
||||
``` ~bob
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed
|
||||
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'followed'
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkuPZ…xEuaPUp..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkvVv…Z1Ct4tD..
|
||||
✓ Creating checkout in ./heartwood..
|
||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Repository successfully cloned under [..]/heartwood/
|
||||
╭────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
│ Radicle Heartwood Protocol & Stack │
|
||||
│ 0 issues · 0 patches │
|
||||
╰────────────────────────────────────╯
|
||||
Run `cd ./heartwood` to go to the repository directory.
|
||||
$ cd ./heartwood
|
||||
$ git push rad master
|
||||
```
|
||||
|
||||
If Alice wants to ensure that both her and Bob need to agree on merges
|
||||
to the default branch, she must set the `threshold` to `2` when adding
|
||||
Bob as a delegate:
|
||||
|
||||
``` ~alice (fails)
|
||||
$ rad id update --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --title "Add Bob" --description "" --threshold 2 --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --no-confirm -q
|
||||
✗ Error: failed to verify delegates for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✗ Error: a threshold of 2 delegates cannot be met, found 1 delegate(s) and the following delegates are missing [did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk]
|
||||
✗ Hint: run `rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk` to follow this missing peer
|
||||
✗ Hint: run `rad sync -f` to attempt to fetch the newly followed peers
|
||||
✗ Error: fatal: refusing to update identity document
|
||||
```
|
||||
|
||||
We can see that `a threshold of 2 delegates cannot be met` when Alice
|
||||
attempts to do this. This is because she requires Bob's default branch
|
||||
to ensure that the threshold can be met and the canonical version of
|
||||
the default branch (`refs/heads/<default branch>` at the top-level of
|
||||
the storage) can be updated.
|
||||
|
||||
So, instead Alice needs to first follow Bob and fetch his references:
|
||||
|
||||
``` ~alice
|
||||
$ rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --alias bob
|
||||
✓ Follow policy updated for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
|
||||
$ rad sync
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkuPZ…xEuaPUp..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkvVv…Z1Ct4tD..
|
||||
✓ Fetched repository from 2 seed(s)
|
||||
✓ Synced with 1 node(s)
|
||||
$ rad id update --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --title "Add Bob" --description "" --threshold 2 --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --no-confirm -q
|
||||
069e7d58faa9a7473d27f5510d676af33282796f
|
||||
$ rad sync
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkuPZ…xEuaPUp..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkvVv…Z1Ct4tD..
|
||||
✓ Fetched repository from 2 seed(s)
|
||||
✓ Synced with 3 node(s)
|
||||
```
|
||||
|
||||
Bob can confirm that he was made a delegate by fetching the update:
|
||||
|
||||
``` ~bob
|
||||
$ rad sync
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkuPZ…xEuaPUp..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkvVv…Z1Ct4tD..
|
||||
✓ Fetched repository from 2 seed(s)
|
||||
✓ Synced with 1 node(s)
|
||||
$ rad inspect --delegates
|
||||
did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
|
||||
did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
|
||||
```
|
||||
|
||||
Later, Bob meets Eve at a conference and she wants to take part in the
|
||||
project. For Bob to propose Eve, similar steps need to happen as
|
||||
between Alice and Bob. Eve first needs to setup a fork:
|
||||
|
||||
``` ~eve
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --scope followed
|
||||
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'followed'
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkuPZ…xEuaPUp..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkvVv…Z1Ct4tD..
|
||||
✓ Creating checkout in ./heartwood..
|
||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Remote bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk added
|
||||
✓ Remote-tracking branch bob@z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk/master created for z6Mkt67…v4N1tRk
|
||||
✓ Repository successfully cloned under [..]/heartwood/
|
||||
╭────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
│ Radicle Heartwood Protocol & Stack │
|
||||
│ 0 issues · 0 patches │
|
||||
╰────────────────────────────────────╯
|
||||
Run `cd ./heartwood` to go to the repository directory.
|
||||
$ cd ./heartwood
|
||||
$ git push rad master
|
||||
```
|
||||
|
||||
Bob then adds Eve as a delegate:
|
||||
|
||||
``` ~bob
|
||||
$ rad id update --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --title "Add Eve" --description "" --delegate did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z --no-confirm -q
|
||||
3cd3c7f9900de0fcb19705856a7cc339a38fb0b3
|
||||
$ rad sync
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkvVv…Z1Ct4tD..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkuPZ…xEuaPUp..
|
||||
✓ Fetched repository from 2 seed(s)
|
||||
✓ Synced with 3 node(s)
|
||||
```
|
||||
|
||||
Notice how there was no need to follow Eve right away in this case?
|
||||
This is because Bob can meet the threshold of 2 without Eve, he
|
||||
has Alice and his default reference.
|
||||
|
||||
Since there are two delegates when Bob adds Eve, Alice needs to accept
|
||||
the change to meet a quorum of votes (`votes >= (delegates / 2) + 1`):
|
||||
|
||||
``` ~alice
|
||||
$ rad sync
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkvVv…Z1Ct4tD..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkuPZ…xEuaPUp..
|
||||
✓ Fetched repository from 2 seed(s)
|
||||
✓ Nothing to announce, already in sync with 3 node(s) (see `rad sync status`)
|
||||
$ rad id list
|
||||
╭────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Status Created │
|
||||
├────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● 3cd3c7f Add Eve bob z6Mkt67…v4N1tRk active now │
|
||||
│ ● 069e7d5 Add Bob alice (you) accepted now │
|
||||
│ ● 0656c21 Initial revision alice (you) accepted now │
|
||||
╰────────────────────────────────────────────────────────────────────────────────╯
|
||||
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --sigrefs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi 6acd3b370839318d96dbfff43948bab2bcdd3681
|
||||
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk c40018821dc1b41cad75e91e0c9d00827e815324
|
||||
$ rad id accept 3cd3c7f
|
||||
✓ Revision 3cd3c7f9900de0fcb19705856a7cc339a38fb0b3 accepted
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Add Eve │
|
||||
│ Revision 3cd3c7f9900de0fcb19705856a7cc339a38fb0b3 │
|
||||
│ Blob 74581605d1f75396c331487a10ca61c4815ed685 │
|
||||
│ Author did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk │
|
||||
│ State accepted │
|
||||
│ Quorum yes │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
|
||||
│ ✓ did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk bob │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
At this point, when Alice runs `rad sync`, she will fetch Eve's fork
|
||||
since she has become a delegate:
|
||||
|
||||
``` ~alice
|
||||
$ rad sync --timeout 3
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkvVv…Z1Ct4tD..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkuPZ…xEuaPUp..
|
||||
✓ Fetched repository from 2 seed(s)
|
||||
✓ Synced with 3 node(s)
|
||||
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --sigrefs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi 1f716870f890be0c13fdd0af9f527af849fec792
|
||||
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk c40018821dc1b41cad75e91e0c9d00827e815324
|
||||
z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z 95cd447c57de8d232c6154f5dba0451aa593520e
|
||||
```
|
||||
|
||||
Since the network is eventually consistent, if Eve decides to `sync`
|
||||
(this could also happen through a transient announcement), then we can
|
||||
see that both seeds are `synced`:
|
||||
|
||||
``` ~eve
|
||||
$ rad sync
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkvVv…Z1Ct4tD..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MkuPZ…xEuaPUp..
|
||||
✓ Fetched repository from 2 seed(s)
|
||||
✓ Synced with 3 node(s)
|
||||
$ rad sync status
|
||||
╭─────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● Node Address Status Tip Timestamp │
|
||||
├─────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● eve (you) 95cd447 now │
|
||||
│ ● bob z6Mkt67…v4N1tRk synced 95cd447 now │
|
||||
│ ● distrustful z6MkvVv…Z1Ct4tD distrustful.radicle.xyz:8776 synced 95cd447 now │
|
||||
│ ● seed z6MkuPZ…xEuaPUp seed.radicle.xyz:8776 synced 95cd447 now │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
@@ -1,104 +0,0 @@
|
||||
First let's add Bob as a delegate, and sync the changes to Bob:
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --title "Add Bob" --description "Add Bob as a delegate" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --threshold 2 -q
|
||||
0ca42d376bd566631083c8913cf86bec722da392
|
||||
```
|
||||
``` ~bob
|
||||
$ cd heartwood
|
||||
$ rad sync --fetch rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
|
||||
One thing that can happen is that two delegates propose a revision at the same
|
||||
time:
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --title "Edit project name" --description "" --payload "xyz.radicle.project" "name" '"heart"' -q
|
||||
12d7300d1bbba84e4e5760c8c61999bf5fefb81a
|
||||
```
|
||||
``` ~bob
|
||||
$ rad id update --title "Edit project name" --description "" --payload "xyz.radicle.project" "name" '"wood"' -q
|
||||
89b2623e7f2ddf5748661b15b9975ab0b4ee17ab
|
||||
```
|
||||
|
||||
When Alice syncs with Bob, she notices the problem: there are two active
|
||||
revisions.
|
||||
|
||||
``` ~alice
|
||||
$ rad sync --fetch rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
$ rad id list
|
||||
╭─────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Status Created │
|
||||
├─────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● 89b2623 Edit project name bob z6Mkt67…v4N1tRk active now │
|
||||
│ ● 12d7300 Edit project name alice (you) active now │
|
||||
│ ● 0ca42d3 Add Bob alice (you) accepted now │
|
||||
│ ● 0656c21 Initial revision alice (you) accepted now │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
This isn't a problem as long as we don't try to accept both. So let's accept
|
||||
Bob's:
|
||||
|
||||
``` ~alice
|
||||
$ rad id accept 89b2623 -q
|
||||
$ rad id list
|
||||
╭─────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Status Created │
|
||||
├─────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● 89b2623 Edit project name bob z6Mkt67…v4N1tRk accepted now │
|
||||
│ ● 12d7300 Edit project name alice (you) stale now │
|
||||
│ ● 0ca42d3 Add Bob alice (you) accepted now │
|
||||
│ ● 0656c21 Initial revision alice (you) accepted now │
|
||||
╰─────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Doing so voided the other conflicting revision, and it can no longer be
|
||||
accepted now.
|
||||
|
||||
``` ~bob
|
||||
$ rad sync --fetch rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
``` ~bob (fail)
|
||||
$ rad id accept 12d7300 -q
|
||||
✗ Error: cannot vote on revision that is stale
|
||||
$ rad id reject 12d7300 -q
|
||||
✗ Error: cannot vote on revision that is stale
|
||||
```
|
||||
``` ~bob
|
||||
$ rad id show 12d7300
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Edit project name │
|
||||
│ Revision 12d7300d1bbba84e4e5760c8c61999bf5fefb81a │
|
||||
│ Blob e93aa3e3c5c448bacd3537a81daf1437eccd046a │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ State stale │
|
||||
│ Quorum no │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice │
|
||||
│ ? did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk bob (you) │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
- "name": "heartwood"
|
||||
+ "name": "heart"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
|
||||
],
|
||||
"threshold": 2
|
||||
}
|
||||
```
|
||||
@@ -1,202 +0,0 @@
|
||||
``` ~alice
|
||||
$ rad id update --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --title "Add Bob" --description "" --threshold 2 --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --no-confirm -q
|
||||
069e7d58faa9a7473d27f5510d676af33282796f
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ rad watch --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --node z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z -r 'refs/rad/sigrefs' -t 95cd447c57de8d232c6154f5dba0451aa593520e -i 500 --timeout 5000
|
||||
$ rad sync --fetch rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
$ rad id --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
╭────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Status Created │
|
||||
├────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● 069e7d5 Add Bob alice z6MknSL…StBU8Vi accepted now │
|
||||
│ ● 0656c21 Initial revision alice z6MknSL…StBU8Vi accepted now │
|
||||
╰────────────────────────────────────────────────────────────────────────────────╯
|
||||
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --sigrefs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi [..]
|
||||
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk [..]
|
||||
z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z [..]
|
||||
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --delegates
|
||||
did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
|
||||
did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
|
||||
$ rad id update --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --title "Add Eve" --description "" --delegate did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z --no-confirm
|
||||
✓ Identity revision 3cd3c7f9900de0fcb19705856a7cc339a38fb0b3 created
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Add Eve │
|
||||
│ Revision 3cd3c7f9900de0fcb19705856a7cc339a38fb0b3 │
|
||||
│ Blob 74581605d1f75396c331487a10ca61c4815ed685 │
|
||||
│ Author did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk │
|
||||
│ State active │
|
||||
│ Quorum no │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk bob (you) │
|
||||
│ ? did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
- "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
|
||||
+ "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk",
|
||||
+ "did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z"
|
||||
],
|
||||
"threshold": 2
|
||||
}
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad sync --fetch rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkux1…nVhib7Z..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk..
|
||||
✓ Fetched repository from 2 seed(s)
|
||||
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --delegates
|
||||
did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
|
||||
did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
|
||||
$ rad id accept 3cd3c7f9900de0fcb19705856a7cc339a38fb0b3 --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --no-confirm
|
||||
✓ Revision 3cd3c7f9900de0fcb19705856a7cc339a38fb0b3 accepted
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Add Eve │
|
||||
│ Revision 3cd3c7f9900de0fcb19705856a7cc339a38fb0b3 │
|
||||
│ Blob 74581605d1f75396c331487a10ca61c4815ed685 │
|
||||
│ Author did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk │
|
||||
│ State accepted │
|
||||
│ Quorum yes │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
|
||||
│ ✓ did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk bob │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --delegates
|
||||
did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi (alice)
|
||||
did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk (bob)
|
||||
did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z (eve)
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --title "Make private" --description "" --visibility private --no-confirm -q
|
||||
e6bf10593b78384eb2b281cbb18a605668a6d1f7
|
||||
```
|
||||
|
||||
We can list all revisions:
|
||||
|
||||
``` ~alice
|
||||
$ rad id list
|
||||
╭────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ ● ID Title Author Status Created │
|
||||
├────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ ● e6bf105 Make private alice (you) active now │
|
||||
│ ● 3cd3c7f Add Eve bob z6Mkt67…v4N1tRk accepted now │
|
||||
│ ● 069e7d5 Add Bob alice (you) accepted now │
|
||||
│ ● 0656c21 Initial revision alice (you) accepted now │
|
||||
╰────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Despite being a delegate, Bob can't edit or redact Alice's revision:
|
||||
|
||||
``` ~bob (fail)
|
||||
$ rad id redact e6bf10593b78384eb2b281cbb18a605668a6d1f7
|
||||
[..]
|
||||
```
|
||||
``` ~bob (fail)
|
||||
$ rad id edit --title "Boo!" --description "Boo!" e6bf10593b78384eb2b281cbb18a605668a6d1f7
|
||||
[..]
|
||||
```
|
||||
|
||||
Alice can edit:
|
||||
|
||||
``` ~alice
|
||||
$ rad id edit --title "Make private" --description "Privacy is cool." e6bf10593b78384eb2b281cbb18a605668a6d1f7
|
||||
✓ Revision e6bf10593b78384eb2b281cbb18a605668a6d1f7 edited
|
||||
$ rad id show e6bf10593b78384eb2b281cbb18a605668a6d1f7
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Make private │
|
||||
│ Revision e6bf10593b78384eb2b281cbb18a605668a6d1f7 │
|
||||
│ Blob c533865b2846ca6c5b4436ec6872257293380c3b │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ State active │
|
||||
│ Quorum no │
|
||||
│ │
|
||||
│ Privacy is cool. │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
|
||||
│ ? did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk bob │
|
||||
│ ? did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z eve │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk",
|
||||
"did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z"
|
||||
],
|
||||
- "threshold": 2
|
||||
+ "threshold": 2,
|
||||
+ "visibility": {
|
||||
+ "type": "private"
|
||||
+ }
|
||||
}
|
||||
```
|
||||
|
||||
And she can redact her revision:
|
||||
|
||||
``` ~alice
|
||||
$ rad id redact e6bf10593b78384eb2b281cbb18a605668a6d1f7
|
||||
✓ Revision e6bf10593b78384eb2b281cbb18a605668a6d1f7 redacted
|
||||
```
|
||||
``` ~alice (fail)
|
||||
$ rad id show e6bf10593b78384eb2b281cbb18a605668a6d1f7
|
||||
✗ Error: revision `e6bf10593b78384eb2b281cbb18a605668a6d1f7` not found
|
||||
```
|
||||
|
||||
Finally, Alice can also propose to remove Bob:
|
||||
``` ~alice
|
||||
$ rad id update --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji --title "Remove Bob" --description "" --rescind did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --no-confirm
|
||||
✓ Identity revision 8ba242a80bc1181f41f9ea7a19286038c7948994 created
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Remove Bob │
|
||||
│ Revision 8ba242a80bc1181f41f9ea7a19286038c7948994 │
|
||||
│ Blob 254d62de237117e7d7b9ceff85c47f5e3b610c1e │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ State active │
|
||||
│ Quorum no │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
|
||||
│ ? did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk bob │
|
||||
│ ? did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z eve │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
- "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk",
|
||||
"did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z"
|
||||
],
|
||||
"threshold": 2
|
||||
}
|
||||
```
|
||||
@@ -1,100 +0,0 @@
|
||||
When we are working with a private repository, we can modify the list of peers
|
||||
we allow by using the `rad id` command with its `--allow` and `--disallow`
|
||||
options. Both options can be specified multiple times in the same command line call:
|
||||
|
||||
Here we will add Bob and Eve's DIDs to the `allow`list:
|
||||
|
||||
```
|
||||
$ rad id update --title "Allow Bob & Eve" --allow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --allow did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z -q
|
||||
...
|
||||
$ rad inspect --identity
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "radicle heartwood protocol & stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
],
|
||||
"threshold": 1,
|
||||
"visibility": {
|
||||
"type": "private",
|
||||
"allow": [
|
||||
"did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk",
|
||||
"did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
To remove a peer's DID, we can use the `--disallow` option. Let's remove both of them again:
|
||||
|
||||
```
|
||||
$ rad id update --title "Remove allow list" --disallow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --disallow did:key:z6Mkux1aUQD2voWWukVb5nNUR7thrHveQG4pDQua8nVhib7Z
|
||||
...
|
||||
$ rad inspect --identity
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "radicle heartwood protocol & stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
],
|
||||
"threshold": 1,
|
||||
"visibility": {
|
||||
"type": "private"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that using both `--disallow` and `--allow` with the same DID will result in
|
||||
an error:
|
||||
|
||||
``` (fails)
|
||||
$ rad id update --title "Remove allow list" --allow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --disallow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
✗ Error: `--allow` and `--disallow` must not overlap: ["did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"]
|
||||
```
|
||||
|
||||
Allowing or disallowing the same peer twice will result in a message saying that
|
||||
the document is already up to date:
|
||||
|
||||
```
|
||||
$ rad id update --title "Allow Bob" --allow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk -q
|
||||
...
|
||||
```
|
||||
```
|
||||
$ rad id update --title "Allow Bob" --allow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
Nothing to do. The document is up to date. See `rad inspect --identity`.
|
||||
```
|
||||
|
||||
If we attempt to change the list while also changing the repository to `public`,
|
||||
then the command will fail since there is no longer an allow list to work with:
|
||||
|
||||
``` (fails)
|
||||
$ rad id update --visibility public --allow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
✗ Error: `--allow` and `--disallow` cannot be used with `--visibility public`
|
||||
```
|
||||
|
||||
Let's change the repository to `public`:
|
||||
|
||||
```
|
||||
$ rad id update --title "IPO" --visibility public -q
|
||||
...
|
||||
```
|
||||
|
||||
Now, if we attempt to change the `allow` list we also get an error with a
|
||||
helpful hint:
|
||||
|
||||
``` (fails)
|
||||
$ rad id update --allow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
✗ Error: `--allow` and `--disallow` should only be used for private repositories
|
||||
✗ Hint: use `--visibility private` to make the repository private, or perhaps you meant to use `--delegate`/`--rescind`
|
||||
```
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
In some cases, a peer can create references, which includes `rad/sigrefs`,
|
||||
without having pushed the canonical default branch. For example, Bob can create
|
||||
an issue in the repository:
|
||||
|
||||
``` ~bob
|
||||
$ rad issue open --title "Add Bob as a delegate" --description "We agreed to add me as a delegate, so I am creating an issue to track that work" --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
╭──────────────────────────────────────────────────────────────╮
|
||||
│ Title Add Bob as a delegate │
|
||||
│ Issue f12d512c51d30429f7916db038ae0360e2e938c2 │
|
||||
│ Author bob (you) │
|
||||
│ Status open │
|
||||
│ │
|
||||
│ We agreed to add me as a delegate, so I am creating an issue │
|
||||
│ to track that work │
|
||||
╰──────────────────────────────────────────────────────────────╯
|
||||
✓ Synced with 1 node(s)
|
||||
```
|
||||
|
||||
and if we inspect Alice's references, then we will see the following:
|
||||
|
||||
``` ~alice
|
||||
$ rad inspect --refs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
└── refs
|
||||
├── cobs
|
||||
│ └── xyz.radicle.id
|
||||
│ └── 0656c217f917c3e06234771e9ecae53aba5e173e
|
||||
├── heads
|
||||
│ └── master
|
||||
└── rad
|
||||
├── id
|
||||
└── sigrefs
|
||||
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
└── refs
|
||||
├── cobs
|
||||
│ └── xyz.radicle.issue
|
||||
│ └── f12d512c51d30429f7916db038ae0360e2e938c2
|
||||
└── rad
|
||||
└── sigrefs
|
||||
```
|
||||
|
||||
Despite not having the canonical branch, Alice should still be able to add Bob
|
||||
as a delegate, since a threshold of 1 can still be reached:
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --title "Add Bob" --description "Add Bob as a delegate" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk -q
|
||||
7be665f9fccba97abb21b2fa85a6fd3181c72858
|
||||
```
|
||||
@@ -1,200 +0,0 @@
|
||||
In the attempt below, to update the identity, we can see that `a
|
||||
threshold of 2 delegates cannot be met` when Alice attempts to do
|
||||
this. This is because she requires Bob's default branch to ensure that
|
||||
the threshold can be met and the canonical version of the default
|
||||
branch (`refs/heads/<default branch>` at the top-level of the storage)
|
||||
can be updated.
|
||||
|
||||
``` ~alice (fail)
|
||||
$ rad id update --title "Add Bob" --description "Add Bob as a delegate" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --threshold 2
|
||||
✗ Error: failed to verify delegates for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✗ Error: the threshold of 2 delegates cannot be met..
|
||||
✗ Error: the delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk is missing
|
||||
✗ Hint: run `rad follow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk` to follow this missing peer
|
||||
✗ Error: fatal: refusing to update identity document
|
||||
```
|
||||
|
||||
Instead, Alice can simply keep the `threshold` as `1` and still add Bob as a delegate:
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --title "Add Bob" --description "Add Bob as a delegate" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
✓ Identity revision 7be665f9fccba97abb21b2fa85a6fd3181c72858 created
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Add Bob │
|
||||
│ Revision 7be665f9fccba97abb21b2fa85a6fd3181c72858 │
|
||||
│ Blob 93d3009787e5d8a481dffc4dd248ea46af592466 │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ State accepted │
|
||||
│ Quorum yes │
|
||||
│ │
|
||||
│ Add Bob as a delegate │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
- "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
+ "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
+ "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
|
||||
],
|
||||
"threshold": 1
|
||||
}
|
||||
```
|
||||
|
||||
Alice can still make changes to her working copy, change the canonical
|
||||
head, and make patches -- as we can see below:
|
||||
|
||||
``` ~alice
|
||||
$ touch REQUIREMENTS
|
||||
$ git add REQUIREMENTS
|
||||
$ git commit -v -m "Define power requirements"
|
||||
[master 3e674d1] Define power requirements
|
||||
1 file changed, 0 insertions(+), 0 deletions(-)
|
||||
create mode 100644 REQUIREMENTS
|
||||
```
|
||||
|
||||
``` ~alice (stderr) RAD_SOCKET=/dev/null
|
||||
$ git push rad master
|
||||
✓ Canonical head updated to 3e674d1a1df90807e934f9ae5da2591dd6848a33
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
f2de534..3e674d1 master -> master
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad sync -a
|
||||
✓ Synced with 1 node(s)
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ git checkout -b add-readme
|
||||
$ touch README.md
|
||||
$ git add README.md
|
||||
$ git commit -v -m "Add README file"
|
||||
[add-readme 964513c] Add README file
|
||||
1 file changed, 0 insertions(+), 0 deletions(-)
|
||||
create mode 100644 README.md
|
||||
```
|
||||
|
||||
``` ~alice (stderr) RAD_SOCKET=/dev/null
|
||||
$ git push rad HEAD:refs/patches
|
||||
✓ Patch b09b2aa0ee055671c811e9ad4ba73eed211ebaa3 opened
|
||||
To rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
* [new reference] HEAD -> refs/patches
|
||||
```
|
||||
|
||||
Any other seeds can also still fetch changes from Alice without any
|
||||
errors:
|
||||
|
||||
``` ~seed
|
||||
$ rad sync rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji -f
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
|
||||
We can also inspect the repository to ensure all the data is
|
||||
consistent with the change made to the identity.
|
||||
|
||||
The identity has the new delegate, while the `threshold` is still `1`
|
||||
|
||||
``` ~alice
|
||||
$ rad inspect --identity
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
|
||||
],
|
||||
"threshold": 1
|
||||
}
|
||||
```
|
||||
|
||||
Alice only sees her refs, since she has not synced with Bob's
|
||||
references yet:
|
||||
|
||||
``` ~alice
|
||||
$ rad inspect --refs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
└── refs
|
||||
├── cobs
|
||||
│ ├── xyz.radicle.id
|
||||
│ │ └── 0656c217f917c3e06234771e9ecae53aba5e173e
|
||||
│ └── xyz.radicle.patch
|
||||
│ └── b09b2aa0ee055671c811e9ad4ba73eed211ebaa3
|
||||
├── heads
|
||||
│ ├── master
|
||||
│ └── patches
|
||||
│ └── b09b2aa0ee055671c811e9ad4ba73eed211ebaa3
|
||||
└── rad
|
||||
├── id
|
||||
└── sigrefs
|
||||
```
|
||||
|
||||
Similarly, she still does not have Bob's `rad/sigrefs`:
|
||||
|
||||
``` ~alice
|
||||
$ rad inspect --sigrefs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi 7ffed605e4871bb0640ee1538181640e239b182c
|
||||
```
|
||||
|
||||
And she can still list the project, without any worries:
|
||||
|
||||
``` ~alice
|
||||
$ rad ls
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Name RID Visibility Head Description │
|
||||
├───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ heartwood rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji public 3e674d1 Radicle Heartwood Protocol & Stack │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Once Bob clones the repository and creates a fork, i.e. creates a
|
||||
branch to `refs/heads/master` for this project, she can then use `rad
|
||||
sync` and fetch his references:
|
||||
|
||||
``` ~bob
|
||||
$ rad clone rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Seeding policy updated for rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji with scope 'all'
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6MknSL…StBU8Vi..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkux1…nVhib7Z..
|
||||
✓ Creating checkout in ./heartwood..
|
||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Repository successfully cloned under [..]/bob/heartwood/
|
||||
╭────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
│ Radicle Heartwood Protocol & Stack │
|
||||
│ 0 issues · 1 patches │
|
||||
╰────────────────────────────────────╯
|
||||
Run `cd ./heartwood` to go to the repository directory.
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ cd heartwood
|
||||
$ rad fork
|
||||
✓ Forked repository rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji for z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad sync -f
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkux1…nVhib7Z..
|
||||
✓ Fetching rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji from z6Mkt67…v4N1tRk..
|
||||
✓ Fetched repository from 2 seed(s)
|
||||
$ rad inspect --sigrefs
|
||||
z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi 7ffed605e4871bb0640ee1538181640e239b182c
|
||||
z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk cc068d93ee77dc134518d7d0fbe55b39804baf53
|
||||
```
|
||||
@@ -1,47 +0,0 @@
|
||||
The payloads in the identity document are extensible and arbitrary fields can be
|
||||
added. Here we will add an emoji alias for the heartwood project:
|
||||
|
||||
```
|
||||
$ rad id update --title "Add emoji alias" --description "Adding alias field" --payload xyz.radicle.project alias '"❤️🪵"'
|
||||
✓ Identity revision 05100d3f0a73b9373681677158615a53ba51940e created
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Add emoji alias │
|
||||
│ Revision 05100d3f0a73b9373681677158615a53ba51940e │
|
||||
│ Blob a0f421c928dcfc6eca129fc2ea1f50877de7dc20 │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ State accepted │
|
||||
│ Quorum yes │
|
||||
│ │
|
||||
│ Adding alias field │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
+ "alias": "❤️🪵",
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
],
|
||||
"threshold": 1
|
||||
}
|
||||
```
|
||||
|
||||
We can see that the project payload still loads by using `rad ls` which will
|
||||
attempt to deserialize the payload:
|
||||
|
||||
```
|
||||
$ rad ls
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Name RID Visibility Head Description │
|
||||
├───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ heartwood rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji public f2de534 Radicle Heartwood Protocol & Stack │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
@@ -1,78 +0,0 @@
|
||||
Let's add a payload field and then delete it.
|
||||
|
||||
```
|
||||
$ rad id update --title "Add field" --description "Add a new 'web' field" --payload xyz.radicle.project web '"https://acme.example"'
|
||||
✓ Identity revision a8a9fee6c4f83578ab132d375f1da0c81282bef3 created
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Add field │
|
||||
│ Revision a8a9fee6c4f83578ab132d375f1da0c81282bef3 │
|
||||
│ Blob fbe268d13e60f1f3a1972e0ccd592f3cdecf08b5 │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ State accepted │
|
||||
│ Quorum yes │
|
||||
│ │
|
||||
│ Add a new 'web' field │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
- "name": "heartwood"
|
||||
+ "name": "heartwood",
|
||||
+ "web": "https://acme.example"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
],
|
||||
"threshold": 1
|
||||
}
|
||||
```
|
||||
|
||||
Now let's delete it by setting it to `null`.
|
||||
|
||||
```
|
||||
$ rad id update --title "Delete field" --description "Delete 'web'" --payload xyz.radicle.project web null
|
||||
✓ Identity revision d373c35876833105f8aafed8b610660b5737cd67 created
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Delete field │
|
||||
│ Revision d373c35876833105f8aafed8b610660b5737cd67 │
|
||||
│ Blob d96f425412c9f8ad5d9a9a05c9831d0728e2338d │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ State accepted │
|
||||
│ Quorum yes │
|
||||
│ │
|
||||
│ Delete 'web' │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
- "name": "heartwood",
|
||||
- "web": "https://acme.example"
|
||||
+ "name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
],
|
||||
"threshold": 1
|
||||
}
|
||||
```
|
||||
|
||||
Note that we cannot delete mandatory fields:
|
||||
|
||||
``` (fails)
|
||||
$ rad id update --title "Delete default branch" --payload xyz.radicle.project defaultBranch null
|
||||
✗ Error: failed to verify `xyz.radicle.project`, json: missing field `defaultBranch`
|
||||
```
|
||||
@@ -1,182 +0,0 @@
|
||||
At some point in the lifetime of a Radicle project you may want to
|
||||
collaborate with someone else allowing them to become a project
|
||||
maintainer. This requires adding them as a `delegate` and possibly
|
||||
editing the `threshold` for passing new changes to the identity of the
|
||||
project.
|
||||
|
||||
For cases where `threshold > 1`, it is necessary to gather a quorum of
|
||||
signatures to update the Radicle identity. To do this, we use the `rad id`
|
||||
command. For now, since we are the only delegate, and `treshold` is `1`, we
|
||||
can update the identity ourselves.
|
||||
|
||||
Let's add Bob as a delegate using their DID,
|
||||
`did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk`, and update the
|
||||
threshold to `2`.
|
||||
|
||||
```
|
||||
$ rad id update --title "Add Bob" --description "Add Bob as a delegate" --delegate did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk --threshold 2
|
||||
✓ Identity revision 0ca42d376bd566631083c8913cf86bec722da392 created
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Add Bob │
|
||||
│ Revision 0ca42d376bd566631083c8913cf86bec722da392 │
|
||||
│ Blob 053541ba7b90534b35dd8718e0ceaa408979b02b │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ State accepted │
|
||||
│ Quorum yes │
|
||||
│ │
|
||||
│ Add Bob as a delegate │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
- "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
+ "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
+ "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
|
||||
],
|
||||
- "threshold": 1
|
||||
+ "threshold": 2
|
||||
}
|
||||
```
|
||||
|
||||
Before moving on, let's take a few notes on this output. The first
|
||||
thing we'll notice is that the difference between the current identity
|
||||
document and the proposed changes are shown. Specifically, we changed
|
||||
the delegates and threshold:
|
||||
|
||||
"delegates": [
|
||||
- "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
+ "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
+ "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
|
||||
],
|
||||
...
|
||||
- "threshold": 1
|
||||
+ "threshold": 2
|
||||
|
||||
Next we have the number of signatures from delegates, which includes our own:
|
||||
|
||||
✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
|
||||
Finally, we can see whether the `Quorum` was reached:
|
||||
|
||||
Quorum yes
|
||||
|
||||
Since the threshold was previously `1`, this change is now in effect. We
|
||||
can verify that by listing the current identity document:
|
||||
|
||||
```
|
||||
$ rad inspect --identity
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
"did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
|
||||
],
|
||||
"threshold": 2
|
||||
}
|
||||
```
|
||||
|
||||
We can also look at the document's COB directly:
|
||||
```
|
||||
$ rad cob log --object 0656c217f917c3e06234771e9ecae53aba5e173e --type xyz.radicle.id --repo rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
commit 0ca42d376bd566631083c8913cf86bec722da392
|
||||
parent 0656c217f917c3e06234771e9ecae53aba5e173e
|
||||
author z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
date Thu, 15 Dec 2022 17:28:04 +0000
|
||||
|
||||
{
|
||||
"blob": "053541ba7b90534b35dd8718e0ceaa408979b02b",
|
||||
"description": "Add Bob as a delegate",
|
||||
"parent": "0656c217f917c3e06234771e9ecae53aba5e173e",
|
||||
"signature": "z3AyzixN2eWLtRfQWowtBXwWyRH3iJ8oJ25W6KFYFw5ANLntbzfavge15muNU6AVAUkxSxQvgg9yh2gupbUecavQY",
|
||||
"title": "Add Bob",
|
||||
"type": "revision"
|
||||
}
|
||||
|
||||
commit 0656c217f917c3e06234771e9ecae53aba5e173e
|
||||
author z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
date Thu, 15 Dec 2022 17:28:04 +0000
|
||||
|
||||
{
|
||||
"blob": "d96f425412c9f8ad5d9a9a05c9831d0728e2338d",
|
||||
"parent": null,
|
||||
"signature": "z5nGqUvrmfiSyLjNCHWTWYvVMcPUZcvo9TxPKzEKXYBdSgUzbrqf1cYsmpGgbQvYunnsrLSsubEmxZaRdKM4quqQR",
|
||||
"title": "Initial revision",
|
||||
"type": "revision"
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
We can use `rad id show` to show the changes of an accepted update:
|
||||
|
||||
```
|
||||
$ rad id show 0ca42d376bd566631083c8913cf86bec722da392
|
||||
╭────────────────────────────────────────────────────────────────────────╮
|
||||
│ Title Add Bob │
|
||||
│ Revision 0ca42d376bd566631083c8913cf86bec722da392 │
|
||||
│ Blob 053541ba7b90534b35dd8718e0ceaa408979b02b │
|
||||
│ Author did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi │
|
||||
│ State accepted │
|
||||
│ Quorum yes │
|
||||
│ │
|
||||
│ Add Bob as a delegate │
|
||||
├────────────────────────────────────────────────────────────────────────┤
|
||||
│ ✓ did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi alice (you) │
|
||||
╰────────────────────────────────────────────────────────────────────────╯
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "Radicle Heartwood Protocol & Stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
- "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
+ "did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi",
|
||||
+ "did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
|
||||
],
|
||||
- "threshold": 1
|
||||
+ "threshold": 2
|
||||
}
|
||||
```
|
||||
|
||||
Note that once a revision is accepted, it can't be edited, redacted or otherwise
|
||||
acted upon:
|
||||
|
||||
``` (fail)
|
||||
$ rad id redact 0ca42d376bd566631083c8913cf86bec722da392
|
||||
✗ Error: [..]
|
||||
```
|
||||
``` (fail)
|
||||
$ rad id reject 0ca42d376bd566631083c8913cf86bec722da392
|
||||
✗ Error: [..]
|
||||
```
|
||||
``` (fail)
|
||||
$ rad id accept 0ca42d376bd566631083c8913cf86bec722da392
|
||||
✗ Error: [..]
|
||||
```
|
||||
|
||||
If no updates are specified then we are told that our command had no effect:
|
||||
|
||||
```
|
||||
$ rad id update --title "Update canonical branch" --description "Update the canonical branch to `main`"
|
||||
Nothing to do. The document is up to date. See `rad inspect --identity`.
|
||||
```
|
||||
@@ -1,140 +0,0 @@
|
||||
``` ~alice
|
||||
$ cd heartwood
|
||||
$ rad inbox
|
||||
Your inbox is empty.
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ cd heartwood
|
||||
$ rad issue open --title "No license file" --description "..." -q
|
||||
✓ Synced with 1 node(s)
|
||||
$ git commit -m "Change copyright" --allow-empty -q
|
||||
$ git push rad HEAD:bob/copy
|
||||
$ cd ..
|
||||
$ cd radicle-git
|
||||
$ git commit -m "Change copyright" --allow-empty -q
|
||||
$ git push rad -o patch.message="Copyright fixes" HEAD:refs/patches
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad inbox --sort-by id
|
||||
╭──────────────────────────────────────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ 001 ● [ ... ] No license file issue open bob now │
|
||||
│ 002 ● bob/copy Change copyright branch created bob now │
|
||||
╰──────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad inbox --all --sort-by id
|
||||
╭────────────────────────────────────────────────────────────────╮
|
||||
│ radicle-git │
|
||||
├────────────────────────────────────────────────────────────────┤
|
||||
│ 003 ● [ ... ] Copyright fixes patch open bob now │
|
||||
╰────────────────────────────────────────────────────────────────╯
|
||||
╭──────────────────────────────────────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ 001 ● [ ... ] No license file issue open bob now │
|
||||
│ 002 ● bob/copy Change copyright branch created bob now │
|
||||
╰──────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad inbox show 2
|
||||
commit 141c9073066e3910f1dfe356904a0120542e1cc9
|
||||
Author: radicle <radicle@localhost>
|
||||
Date: Thu Dec 15 17:28:04 2022 +0000
|
||||
|
||||
Change copyright
|
||||
|
||||
commit f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
|
||||
Author: anonymous <anonymous@radicle.xyz>
|
||||
Date: Mon Jan 1 14:39:16 2018 +0000
|
||||
|
||||
Second commit
|
||||
|
||||
commit 08c788dd1be6315de09e3fe09b5b1b7a2b8711d9
|
||||
Author: anonymous <anonymous@radicle.xyz>
|
||||
Date: Mon Jan 1 14:39:16 2018 +0000
|
||||
|
||||
Initial commit
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad inbox list --sort-by id
|
||||
╭──────────────────────────────────────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ 001 ● [ ... ] No license file issue open bob now │
|
||||
│ 002 bob/copy Change copyright branch created bob now │
|
||||
╰──────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad inbox show 1
|
||||
╭──────────────────────────────────────────────────╮
|
||||
│ Title No license file │
|
||||
│ Issue [ ... ] │
|
||||
│ Author bob z6Mkt67…v4N1tRk │
|
||||
│ Status open │
|
||||
│ │
|
||||
│ ... │
|
||||
╰──────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad inbox clear 1 2
|
||||
✓ Cleared 2 item(s) from your inbox
|
||||
$ rad inbox
|
||||
Your inbox is empty.
|
||||
$ rad inbox --all
|
||||
╭────────────────────────────────────────────────────────────────╮
|
||||
│ radicle-git │
|
||||
├────────────────────────────────────────────────────────────────┤
|
||||
│ 003 ● [ ... ] Copyright fixes patch open bob now │
|
||||
╰────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad inbox clear --all
|
||||
✓ Cleared 1 item(s) from your inbox
|
||||
```
|
||||
|
||||
``` ~alice
|
||||
$ rad inbox clear --all
|
||||
Your inbox is empty.
|
||||
```
|
||||
|
||||
Now let's do an identity update.
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --title "Modify description" --description "Use website" --payload xyz.radicle.project description '"https://radicle.xyz"' -q
|
||||
[..]
|
||||
$ rad sync -a
|
||||
✓ Synced with 1 node(s)
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ rad inbox --all
|
||||
╭──────────────────────────────────────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
├──────────────────────────────────────────────────────────────────────┤
|
||||
│ 001 ● [ ... ] Modify description id accepted alice now │
|
||||
╰──────────────────────────────────────────────────────────────────────╯
|
||||
$ rad inbox show 1
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "https://radicle.xyz",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
],
|
||||
"threshold": 1
|
||||
}
|
||||
```
|
||||
@@ -1,40 +0,0 @@
|
||||
Let's clone a regular repository via plain Git:
|
||||
```
|
||||
$ git clone $URL heartwood
|
||||
$ cd heartwood
|
||||
$ git rev-parse HEAD
|
||||
f2de534b5e81d7c6e2dcaf58c3dd91573c0a0354
|
||||
```
|
||||
|
||||
We can see it's not a Radicle working copy:
|
||||
``` (fail)
|
||||
$ rad .
|
||||
✗ Error: Current directory is not a Radicle repository
|
||||
```
|
||||
|
||||
Let's pick an existing repository:
|
||||
```
|
||||
$ rad inspect rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
```
|
||||
|
||||
And initialize this working copy as that existing repository:
|
||||
```
|
||||
$ rad init --existing rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
✓ Initialized existing repository rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji in [..]/heartwood/..
|
||||
```
|
||||
|
||||
We can confirm that the working copy is initialized:
|
||||
```
|
||||
$ rad .
|
||||
rad:z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
$ git remote show rad
|
||||
* remote rad
|
||||
Fetch URL: rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji
|
||||
Push URL: rad://z42hL2jL4XNk6K8oHQaSWfMgCL7ji/z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
HEAD branch: (unknown)
|
||||
Remote branch:
|
||||
master new (next fetch will store in remotes/rad)
|
||||
Local ref configured for 'git push':
|
||||
master pushes to master (up to date)
|
||||
```
|
||||
@@ -1,22 +0,0 @@
|
||||
If you try to `init` from a directory that doesn't contain a Git repository,
|
||||
it will fail:
|
||||
|
||||
``` (fail)
|
||||
$ rad init
|
||||
✗ Error: a Git repository was not found at the given path
|
||||
```
|
||||
|
||||
Ok so let's initialize one.
|
||||
|
||||
```
|
||||
$ git init -q
|
||||
```
|
||||
|
||||
Now we try again.
|
||||
|
||||
``` (fail)
|
||||
$ rad init
|
||||
✗ Error: repository head must point to a commit
|
||||
```
|
||||
|
||||
Looks like we need a commit.
|
||||
@@ -1,29 +0,0 @@
|
||||
If we initialize a public repository without seeding it, it won't be advertized:
|
||||
```
|
||||
$ rad init --name heartwood --description "radicle heartwood protocol & stack" --no-confirm --public --no-seed
|
||||
|
||||
Initializing public radicle 👾 repository in [..]
|
||||
|
||||
✓ Repository heartwood created.
|
||||
|
||||
Your Repository ID (RID) is rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK.
|
||||
You can show it any time by running `rad .` from this directory.
|
||||
|
||||
Your repository will be announced to the network when you start your node.
|
||||
You can start your node with `rad node start`.
|
||||
To push changes, run `git push`.
|
||||
```
|
||||
```
|
||||
$ rad node inventory
|
||||
```
|
||||
|
||||
If we then seed it, it becomes advertized in our inventory:
|
||||
```
|
||||
$ rad seed rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK
|
||||
✓ Inventory updated with rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK
|
||||
✓ Seeding policy updated for rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK with scope 'all'
|
||||
```
|
||||
```
|
||||
$ rad node inventory
|
||||
rad:zhbMU4DUXrzB8xT6qAJh6yZ7bFMK
|
||||
```
|
||||
@@ -1,53 +0,0 @@
|
||||
Given a private repo `rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu` belonging to Alice,
|
||||
Alice allows Bob to fetch it, and Bob, without the updated identity document
|
||||
is able to fetch it by specifiying Alice as a seed.
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --title "Allow Bob" --description "" --allow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk -q
|
||||
...
|
||||
$ rad inspect --identity
|
||||
{
|
||||
"payload": {
|
||||
"xyz.radicle.project": {
|
||||
"defaultBranch": "master",
|
||||
"description": "radicle heartwood protocol & stack",
|
||||
"name": "heartwood"
|
||||
}
|
||||
},
|
||||
"delegates": [
|
||||
"did:key:z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi"
|
||||
],
|
||||
"threshold": 1,
|
||||
"visibility": {
|
||||
"type": "private",
|
||||
"allow": [
|
||||
"did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk"
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ rad ls --all --private
|
||||
$ rad clone rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --seed z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --timeout 1
|
||||
✓ Seeding policy updated for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu with scope 'all'
|
||||
✓ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi..
|
||||
✓ Creating checkout in ./heartwood..
|
||||
✓ Remote alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi added
|
||||
✓ Remote-tracking branch alice@z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi/master created for z6MknSL…StBU8Vi
|
||||
✓ Repository successfully cloned under [...]/.radicle/heartwood/
|
||||
╭────────────────────────────────────╮
|
||||
│ heartwood │
|
||||
│ radicle heartwood protocol & stack │
|
||||
│ 0 issues · 0 patches │
|
||||
╰────────────────────────────────────╯
|
||||
Run `cd ./heartwood` to go to the repository directory.
|
||||
```
|
||||
|
||||
We can also use `rad seed` to seed and fetch without creating a checkout.
|
||||
|
||||
``` ~bob
|
||||
$ rad seed rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --from z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --timeout 1
|
||||
✓ Seeding policy exists for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu with scope 'all'
|
||||
✓ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi..
|
||||
```
|
||||
@@ -1,44 +0,0 @@
|
||||
Given a private repo `rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu` belonging to Alice,
|
||||
Bob tries to fetch it, and even though he's connected to Alice, it fails.
|
||||
|
||||
``` ~bob
|
||||
$ rad ls
|
||||
```
|
||||
``` ~bob (fail)
|
||||
$ rad clone rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --seed z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --timeout 1
|
||||
✓ Seeding policy updated for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu with scope 'all'
|
||||
✗ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi.. error: failed to perform fetch handshake
|
||||
✗ Error: repository rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu not found
|
||||
```
|
||||
|
||||
She allows Bob to view the repository. And when she syncs, one node (Bob) gets
|
||||
the refs.
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --title "Allow Bob" --description "" --allow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk -q
|
||||
...
|
||||
$ rad sync --announce --timeout 3
|
||||
✓ Synced with 1 node(s)
|
||||
```
|
||||
|
||||
Bob can now fetch the private repo without specifying a seed, because he knows
|
||||
that Alice has the repo after she announced her refs:
|
||||
|
||||
``` ~bob
|
||||
$ rad sync rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --fetch
|
||||
✓ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
$ rad ls --private --all
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Name RID Visibility Head Description │
|
||||
├───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ heartwood rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu private f2de534 radicle heartwood protocol & stack │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Note that since we don't have our own fork of this repo, omitting the `--all` flag shows nothing:
|
||||
|
||||
``` ~bob
|
||||
$ rad ls --private
|
||||
Nothing to show.
|
||||
```
|
||||
@@ -1,37 +0,0 @@
|
||||
Let's say we initialize a private repository and specify that we don't want it
|
||||
to be seeded. This means that the repo will be available locally, to us,
|
||||
and even if other peers know about it, they won't be able to fetch it
|
||||
from us.
|
||||
```
|
||||
$ rad init --name heartwood --description "radicle heartwood protocol & stack" --no-confirm --private --no-seed
|
||||
|
||||
Initializing private radicle 👾 repository in [..]
|
||||
|
||||
✓ Repository heartwood created.
|
||||
|
||||
Your Repository ID (RID) is rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu.
|
||||
You can show it any time by running `rad .` from this directory.
|
||||
|
||||
You have created a private repository.
|
||||
This repository will only be visible to you, and to peers you explicitly allow.
|
||||
|
||||
To make it public, run `rad publish`.
|
||||
To push changes, run `git push`.
|
||||
```
|
||||
|
||||
```
|
||||
$ rad seed
|
||||
No seeding policies to show.
|
||||
```
|
||||
|
||||
We can decide to seed it later, so that others can fetch it from us, given
|
||||
that they are part of the allow list:
|
||||
```
|
||||
$ rad seed rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu
|
||||
✓ Seeding policy updated for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu with scope 'all'
|
||||
```
|
||||
|
||||
But it still won't show up in our inventory, since it's private:
|
||||
```
|
||||
$ rad node inventory
|
||||
```
|
||||
@@ -1,48 +0,0 @@
|
||||
Alice allows Bob to fetch this repo, but doesn't announce it, which means
|
||||
that Bob needs to know to fetch it from Alice.
|
||||
|
||||
``` ~alice
|
||||
$ rad id update --title "Allow Bob" --description "" --allow did:key:z6Mkt67GdsW7715MEfRuP4pSZxJRJh6kj6Y48WRqVv4N1tRk -q
|
||||
[..]
|
||||
```
|
||||
|
||||
First, Bob seeds the repo.
|
||||
|
||||
``` ~bob
|
||||
$ rad seed rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --no-fetch
|
||||
[..]
|
||||
```
|
||||
|
||||
If Bob just tries to fetch it without specifying seeds, he gets an error:
|
||||
|
||||
``` ~bob
|
||||
$ rad sync rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --fetch
|
||||
✗ Error: no seeds found for rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu
|
||||
```
|
||||
|
||||
He has to specify a seed that isn't in his routing table:
|
||||
|
||||
``` ~bob
|
||||
$ rad sync rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --fetch --seed z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi
|
||||
✓ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi..
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
|
||||
``` ~bob
|
||||
$ rad ls --private --all
|
||||
╭───────────────────────────────────────────────────────────────────────────────────────────────────────────╮
|
||||
│ Name RID Visibility Head Description │
|
||||
├───────────────────────────────────────────────────────────────────────────────────────────────────────────┤
|
||||
│ heartwood rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu private f2de534 radicle heartwood protocol & stack │
|
||||
╰───────────────────────────────────────────────────────────────────────────────────────────────────────────╯
|
||||
```
|
||||
|
||||
Note that if multiple seeds are specified, the command succeeds as long as one
|
||||
seed succeeds.
|
||||
|
||||
``` ~bob
|
||||
$ rad sync rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu --fetch --seed z6MknSLrJoTcukLrE435hVNQT4JUhbvWLX4kUzqkEStBU8Vi --seed z6MkwPUeUS2fJMfc2HZN1RQTQcTTuhw4HhPySB8JeUg2mVvx
|
||||
✓ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MknSL…StBU8Vi..
|
||||
✗ Fetching rad:z2ug5mwNKZB8KGpBDRTrWHAMbvHCu from z6MkwPU…Ug2mVvx.. error: peer is not connected; cannot initiate fetch
|
||||
✓ Fetched repository from 1 seed(s)
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user