Log from Radicle native CI

Request message

{
  "request": "trigger",
  "version": 1,
  "event_type": "push",
  "repository": {
    "id": "rad:z3gqcJUoA1n9HaHKufZs5FCSGazv5",
    "name": "heartwood",
    "description": "Radicle Heartwood Protocol & Stack",
    "private": false,
    "default_branch": "master",
    "delegates": [
      "did:key:z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT",
      "did:key:z6MktaNvN1KVFMkSRAiN4qK5yvX1zuEEaseeX5sffhzPZRZW",
      "did:key:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM"
    ]
  },
  "pusher": {
    "id": "did:key:z6MkhBHBqPQEag1YAfdUCA1BmHVhSY1Cx7hXFwssw112vzsP",
    "alias": "seed.cheerios.gr"
  },
  "before": "0d402647cb2eb98e4fc97c3a60ab3c6cba9152d0",
  "after": "0d402647cb2eb98e4fc97c3a60ab3c6cba9152d0",
  "branch": "",
  "commits": [
    "0d402647cb2eb98e4fc97c3a60ab3c6cba9152d0"
  ]
}

.radicle/native.yaml

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

Table of contents

Run: git clone /home/_rad/.radicle/storage/z3gqcJUoA1n9HaHKufZs5FCSGazv5 /srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src

Command arguments:

In directory: /

Exit code: 0

Output (stdout and stderr):

Cloning into '/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src'...
done.

Run: git config advice.detachedHead false

Command arguments:

In directory: /srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src

Exit code: 0

Run: git checkout 0d402647cb2eb98e4fc97c3a60ab3c6cba9152d0

Command arguments:

In directory: /srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src

Exit code: 0

Output (stdout and stderr):

HEAD is now at 0d402647cb2 term: allow Editor to be reusable

Run: git show 0d402647cb2eb98e4fc97c3a60ab3c6cba9152d0

Command arguments:

In directory: /srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src

Exit code: 0

Output (stdout and stderr):

commit 0d402647cb2eb98e4fc97c3a60ab3c6cba9152d0
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date:   Wed Nov 6 14:14:20 2024 +0000

    term: allow Editor to be reusable
    
    The `Editor` is very useful for correctly opening a text editor and making changes
    to some initial input. The current use of `Editor` is only for getting input for
    commets.
    
    However, it would also be useful for opening up a text editor on some other
    existing files or text, for example, in the command `rad config edit`.
    
    The `Editor` struct was changed to have two new options, `truncate` and
    `cleanup`, to allow the user of the struct to dictate whether existing text is
    truncated, and if the underlying file should be remove.
    
    The original `new` method is now named `comment`, mimicing the existing
    construction of a temporary `RAD_COMMENT` file.
    
    The new version of `new` accepts any file for the `Editor` and will open it
    without truncating or removing the file.
    
    `Editor` is now used for the `rad config edit` command.

diff --git a/radicle-cli/src/commands/config.rs b/radicle-cli/src/commands/config.rs
index 56f00c92e2c..95d2748942e 100644
--- a/radicle-cli/src/commands/config.rs
+++ b/radicle-cli/src/commands/config.rs
@@ -1,7 +1,6 @@
 #![allow(clippy::or_fun_call)]
 use std::ffi::OsString;
 use std::path::Path;
-use std::process;
 use std::str::FromStr;
 
 use anyhow::anyhow;
@@ -188,18 +187,12 @@ pub fn run(options: Options, ctx: impl term::Context) -> anyhow::Result<()> {
                 path.display()
             );
         }
-        Operation::Edit => {
-            let Some(cmd) = term::editor::default_editor() else {
-                anyhow::bail!("no editor configured; please set the `EDITOR` environment variable");
-            };
-            process::Command::new(cmd)
-                .stdout(process::Stdio::inherit())
-                .stderr(process::Stdio::inherit())
-                .stdin(process::Stdio::inherit())
-                .arg(&path)
-                .spawn()?
-                .wait()?;
-        }
+        Operation::Edit => match term::editor::Editor::new(&path)?.extension("json").edit()? {
+            Some(_) => {
+                term::success!("Successfully made changes to the configuration at {path:?}")
+            }
+            None => term::info!("No changes were made to the configuration at {path:?}"),
+        },
     }
 
     Ok(())
diff --git a/radicle-cli/src/commands/patch/review/builder.rs b/radicle-cli/src/commands/patch/review/builder.rs
index f99911ff3bf..8a5f91baaf5 100644
--- a/radicle-cli/src/commands/patch/review/builder.rs
+++ b/radicle-cli/src/commands/patch/review/builder.rs
@@ -853,7 +853,10 @@ impl CommentBuilder {
         for line in hunk.to_unified_string()?.lines() {
             writeln!(&mut input, "> {line}")?;
         }
-        let output = term::Editor::new().extension("diff").edit(input)?;
+        let output = term::Editor::comment()
+            .extension("diff")
+            .initial(input)?
+            .edit()?;
 
         if let Some(output) = output {
             let header = HunkHeader::try_from(hunk)?;
diff --git a/radicle-cli/src/terminal/patch.rs b/radicle-cli/src/terminal/patch.rs
index cb2efa24bc0..3a40bb37230 100644
--- a/radicle-cli/src/terminal/patch.rs
+++ b/radicle-cli/src/terminal/patch.rs
@@ -51,7 +51,10 @@ impl Message {
         let comment = match self {
             Message::Edit => {
                 if io::stderr().is_terminal() {
-                    term::Editor::new().extension("markdown").edit(help)?
+                    term::Editor::comment()
+                        .extension("markdown")
+                        .initial(help)?
+                        .edit()?
                 } else {
                     Some(help.to_owned())
                 }
diff --git a/radicle-term/src/editor.rs b/radicle-term/src/editor.rs
index c4e7bab0b35..3f255b4bc90 100644
--- a/radicle-term/src/editor.rs
+++ b/radicle-term/src/editor.rs
@@ -13,26 +13,52 @@ pub const PATHS: &[&str] = &["/usr/local/bin", "/usr/bin", "/bin"];
 /// Allows for text input in the configured editor.
 pub struct Editor {
     path: PathBuf,
+    truncate: bool,
+    cleanup: bool,
 }
 
-impl Drop for Editor {
-    fn drop(&mut self) {
-        fs::remove_file(&self.path).ok();
+impl Default for Editor {
+    fn default() -> Self {
+        Self::comment()
     }
 }
 
-impl Default for Editor {
-    fn default() -> Self {
-        Self::new()
+impl Drop for Editor {
+    fn drop(&mut self) {
+        if self.cleanup {
+            fs::remove_file(&self.path).ok();
+        }
     }
 }
 
 impl Editor {
     /// Create a new editor.
-    pub fn new() -> Self {
+    pub fn new(path: impl AsRef<Path>) -> io::Result<Self> {
+        let path = path.as_ref();
+        if path.try_exists()? {
+            let meta = fs::metadata(path)?;
+            if !meta.is_file() {
+                return Err(io::Error::new(
+                    io::ErrorKind::InvalidInput,
+                    "must be used to edit a file",
+                ));
+            }
+        }
+        Ok(Self {
+            path: path.to_path_buf(),
+            truncate: false,
+            cleanup: false,
+        })
+    }
+
+    pub fn comment() -> Self {
         let path = env::temp_dir().join(COMMENT_FILE);
 
-        Self { path }
+        Self {
+            path,
+            truncate: true,
+            cleanup: true,
+        }
     }
 
     /// Set the file extension.
@@ -43,26 +69,43 @@ impl Editor {
         self
     }
 
-    /// Open the editor and return the edited text.
-    ///
-    /// If the text hasn't changed from the initial contents of the editor,
-    /// return `None`.
-    pub fn edit(&mut self, initial: impl AsRef<[u8]>) -> io::Result<Option<String>> {
-        let initial = initial.as_ref();
+    /// Truncate the file to length 0 when opening
+    pub fn truncate(mut self, truncate: bool) -> Self {
+        self.truncate = truncate;
+        self
+    }
+
+    /// Clean up the file after the [`Editor`] is dropped.
+    pub fn cleanup(mut self, cleanup: bool) -> Self {
+        self.cleanup = cleanup;
+        self
+    }
+
+    /// Initialize the file with the provided `content`, as long as the file
+    /// does not already contain anything.
+    pub fn initial(self, content: impl AsRef<[u8]>) -> io::Result<Self> {
+        let content = content.as_ref();
         let mut file = fs::OpenOptions::new()
             .write(true)
             .create(true)
-            .truncate(true)
+            .truncate(self.truncate)
             .open(&self.path)?;
 
         if file.metadata()?.len() == 0 {
-            file.write_all(initial)?;
-            if !initial.ends_with(&[b'\n']) {
+            file.write_all(content)?;
+            if !content.ends_with(&[b'\n']) {
                 file.write_all(b"\n")?;
             }
             file.flush()?;
         }
+        Ok(self)
+    }
 
+    /// Open the editor and return the edited text.
+    ///
+    /// If the text hasn't changed from the initial contents of the editor,
+    /// return `None`.
+    pub fn edit(&mut self) -> io::Result<Option<String>> {
         let Some(cmd) = self::default_editor() else {
             return Err(io::Error::new(
                 io::ErrorKind::NotFound,
@@ -126,7 +169,7 @@ impl Editor {
 }
 
 /// Get the default editor command.
-pub fn default_editor() -> Option<OsString> {
+fn default_editor() -> Option<OsString> {
     // First check the standard environment variables.
     if let Ok(visual) = env::var("VISUAL") {
         if !visual.is_empty() {
diff --git a/radicle-tools/src/rad-cli-demo.rs b/radicle-tools/src/rad-cli-demo.rs
index 36caf31875a..5630f526d40 100644
--- a/radicle-tools/src/rad-cli-demo.rs
+++ b/radicle-tools/src/rad-cli-demo.rs
@@ -35,9 +35,10 @@ fn main() -> anyhow::Result<()> {
             radicle_term::pager::page(table)?;
         }
         "editor" => {
-            let output = terminal::editor::Editor::new()
+            let output = terminal::editor::Editor::comment()
                 .extension("rs")
-                .edit("// Enter code here.");
+                .initial("// Enter code here.")?
+                .edit();
 
             match output {
                 Ok(Some(s)) => {

Run: timeout 600 bash -c set -xeuo pipefail 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

Command arguments:

In directory: /srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src

Exit code: 101

Output (stdout and stderr):

+ cargo --version
cargo 1.80.1 (376290515 2024-07-16)
+ rustc --version
rustc 1.80.1 (3f5fd8dd4 2024-08-06)
+ cargo fmt --check
+ cargo clippy --all-targets --workspace -- --deny clippy::all
   Compiling libc v0.2.155
   Compiling proc-macro2 v1.0.81
   Compiling unicode-ident v1.0.12
    Checking cfg-if v1.0.0
   Compiling once_cell v1.19.0
   Compiling version_check v0.9.4
    Checking memchr v2.7.2
    Checking regex-syntax v0.8.3
   Compiling thiserror v1.0.59
   Compiling typenum v1.17.0
    Checking tinyvec_macros v0.1.1
    Checking fastrand v2.1.0
    Checking subtle v2.5.0
   Compiling syn v1.0.109
    Checking zeroize v1.7.0
    Checking cpufeatures v0.2.12
    Checking bitflags v2.5.0
    Checking tinyvec v1.6.0
   Compiling pkg-config v0.3.30
   Compiling autocfg v1.2.0
   Compiling crc32fast v1.4.0
    Checking percent-encoding v2.3.1
    Checking unicode-bidi v0.3.15
   Compiling vcpkg v0.2.15
    Checking form_urlencoded v1.2.1
   Compiling serde v1.0.198
   Compiling generic-array v0.14.7
    Checking opaque-debug v0.3.1
   Compiling data-encoding v2.5.0
   Compiling rustix v0.38.34
    Checking byteorder v1.5.0
    Checking log v0.4.21
    Checking aho-corasick v1.1.3
    Checking ascii v1.1.0
    Checking itoa v1.0.11
    Checking amplify_num v0.5.2
    Checking linux-raw-sys v0.4.13
    Checking signature v1.6.4
   Compiling proc-macro-error-attr v1.0.4
    Checking ed25519 v1.5.3
    Checking ct-codecs v1.1.1
    Checking unicode-normalization v0.1.23
   Compiling quote v1.0.36
    Checking base-x v0.2.11
   Compiling proc-macro-error v1.0.4
    Checking keccak v0.1.5
    Checking base64ct v1.6.0
    Checking hashbrown v0.14.3
    Checking base32 v0.4.0
   Compiling jobserver v0.1.31
   Compiling syn v2.0.60
    Checking getrandom v0.2.14
    Checking ppv-lite86 v0.2.17
    Checking pem-rfc7468 v0.7.0
   Compiling cc v1.0.95
    Checking rand_core v0.6.4
    Checking ec25519 v0.1.0
    Checking idna v0.5.0
   Compiling crossbeam-utils v0.8.19
    Checking radicle-std-ext v0.1.0
    Checking signature v2.2.0
   Compiling num-traits v0.2.18
    Checking rand_chacha v0.3.1
    Checking equivalent v1.0.1
   Compiling serde_json v1.0.116
    Checking ryu v1.0.17
    Checking indexmap v2.2.6
    Checking rand v0.8.5
    Checking radicle-dag v0.9.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-dag)
   Compiling anyhow v1.0.82
    Checking iana-time-zone v0.1.60
    Checking lazy_static v1.4.0
    Checking anstyle-query v1.0.2
    Checking url v2.5.0
    Checking colored v2.1.0
    Checking regex-automata v0.4.6
    Checking siphasher v1.0.1
    Checking crypto-common v0.1.6
    Checking block-padding v0.3.3
    Checking block-buffer v0.10.4
    Checking universal-hash v0.5.1
    Checking aead v0.5.2
    Checking inout v0.1.3
    Checking base64 v0.21.7
    Checking crossbeam-channel v0.5.13
    Checking digest v0.10.7
    Checking utf8parse v0.2.1
    Checking poly1305 v0.8.0
    Checking polyval v0.6.2
    Checking anstyle-parse v0.2.3
    Checking cipher v0.4.4
    Checking anstyle v1.0.6
    Checking ghash v0.5.1
    Checking sha2 v0.10.8
    Checking hmac v0.12.1
    Checking sha3 v0.10.8
    Checking colorchoice v1.0.0
    Checking normalize-line-endings v0.3.0
    Checking similar v2.5.0
    Checking qcheck v1.0.0
    Checking anstream v0.6.13
    Checking chacha20 v0.9.1
    Checking pbkdf2 v0.12.2
    Checking ctr v0.9.2
    Checking aes v0.8.4
    Checking cbc v0.1.2
    Checking blowfish v0.9.1
    Checking chacha20poly1305 v0.10.1
    Checking radicle-signals v0.10.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-signals)
    Checking ssh-encoding v0.2.0
    Checking snapbox-macros v0.3.8
    Checking bcrypt-pbkdf v0.10.0
    Checking diff v0.1.13
    Checking numtoa v0.1.0
    Checking unicode-segmentation v1.11.0
    Checking aes-gcm v0.10.3
    Checking yansi v0.5.1
    Checking gix-trace v0.1.9
   Compiling adler v1.0.2
   Compiling filetime v0.2.23
    Checking ssh-cipher v0.2.0
    Checking snapbox v0.4.17
   Compiling miniz_oxide v0.7.2
    Checking tempfile v3.10.1
    Checking pretty_assertions v1.4.0
    Checking lexopt v0.3.0
   Compiling escargot v0.5.10
    Checking shlex v1.3.0
    Checking termion v2.0.3
    Checking ssh-key v0.6.6
    Checking fxhash v0.2.1
    Checking unicode-width v0.1.11
    Checking chrono v0.4.38
    Checking newline-converter v0.3.0
    Checking faster-hex v0.9.0
   Compiling libz-sys v1.1.16
   Compiling libgit2-sys v0.17.0+1.8.1
   Compiling sqlite3-src v0.5.1
   Compiling tree-sitter v0.20.10
    Checking dyn-clone v1.0.17
   Compiling amplify_syn v2.0.1
   Compiling flate2 v1.0.28
    Checking inquire v0.7.5
   Compiling tree-sitter-ruby v0.20.1
   Compiling tree-sitter-json v0.20.2
   Compiling tree-sitter-md v0.1.7
   Compiling tree-sitter-c v0.20.8
   Compiling tree-sitter-python v0.20.4
   Compiling tree-sitter-rust v0.20.4
   Compiling tree-sitter-css v0.20.0
   Compiling tree-sitter-html v0.20.0
   Compiling tree-sitter-go v0.20.0
   Compiling tree-sitter-toml v0.20.0
   Compiling tree-sitter-bash v0.20.5
    Checking bstr v1.9.1
    Checking regex v1.10.4
   Compiling tree-sitter-typescript v0.20.5
    Checking unicode-display-width v0.3.0
    Checking termion v3.0.0
    Checking gix-utils v0.1.12
   Compiling xattr v1.3.1
   Compiling tar v0.4.40
    Checking same-file v1.0.6
   Compiling radicle-cli v0.11.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-cli)
    Checking base64 v0.13.1
    Checking nonempty v0.5.0
   Compiling data-encoding-macro-internal v0.1.12
   Compiling amplify_derive v4.0.0
    Checking walkdir v2.5.0
   Compiling thiserror-impl v1.0.59
   Compiling serde_derive v1.0.198
   Compiling radicle-surf v0.22.0
    Checking sha1_smol v1.0.0
    Checking prodash v28.0.0
    Checking data-encoding-macro v0.1.14
    Checking timeago v0.4.2
    Checking multibase v0.9.1
    Checking smallvec v1.13.2
   Compiling lock_api v0.4.11
   Compiling parking_lot_core v0.9.9
    Checking powerfmt v0.2.0
   Compiling time-core v0.1.2
   Compiling num-conv v0.1.0
    Checking scopeguard v1.2.0
    Checking deranged v0.3.11
   Compiling time-macros v0.2.18
    Checking home v0.5.9
    Checking num_threads v0.1.7
    Checking parking_lot v0.12.1
    Checking winnow v0.6.8
    Checking memmap2 v0.9.4
    Checking shell-words v1.1.0
    Checking gix-sec v0.10.6
   Compiling maybe-async v0.2.10
    Checking arc-swap v1.7.1
    Checking popol v3.0.0
    Checking either v1.11.0
    Checking salsa20 v0.10.2
    Checking socket2 v0.5.7
    Checking siphasher v0.3.11
   Compiling radicle-node v0.10.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-node)
    Checking bloomy v1.2.0
    Checking scrypt v0.11.0
    Checking radicle-systemd v0.9.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-systemd)
    Checking radicle-ssh v0.9.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-ssh)
    Checking gix-hash v0.14.2
   Compiling git-ref-format-core v0.3.0
    Checking tree-sitter-highlight v0.20.1
    Checking gix-features v0.38.2
    Checking gix-path v0.10.9
    Checking gix-validate v0.8.5
    Checking gix-chunk v0.4.8
    Checking gix-hashtable v0.5.2
    Checking gix-command v0.3.6
    Checking gix-config-value v0.14.6
    Checking gix-url v0.27.3
   Compiling git-ref-format-macro v0.3.0
    Checking gix-commitgraph v0.24.3
    Checking gix-fs v0.11.2
    Checking gix-quote v0.4.12
    Checking gix-packetline v0.17.5
    Checking gix-prompt v0.8.4
    Checking gix-tempfile v14.0.1
   Compiling qcheck-macros v1.0.0
    Checking gix-credentials v0.24.2
    Checking gix-transport v0.42.0
   Compiling radicle-remote-helper v0.10.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-remote-helper)
    Checking amplify v4.6.0
    Checking time v0.3.36
    Checking cyphergraphy v0.3.0
    Checking io-reactor v0.5.2
    Checking cypheraddr v0.4.0
    Checking noise-framework v0.4.0
    Checking socks5-client v0.4.1
    Checking cyphernet v0.5.2
    Checking netservices v0.8.0
    Checking gix-date v0.8.7
    Checking gix-actor v0.31.5
    Checking gix-protocol v0.45.0
    Checking gix-object v0.42.3
    Checking gix-revwalk v0.13.2
    Checking gix-diff v0.44.1
    Checking gix-traverse v0.39.2
    Checking gix-pack v0.51.1
    Checking nonempty v0.9.0
    Checking localtime v1.3.1
    Checking git-ref-format v0.3.0
    Checking gix-odb v0.61.1
    Checking git2 v0.19.0
    Checking radicle-git-ext v0.8.0
    Checking radicle-term v0.11.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-term)
    Checking sqlite3-sys v0.15.2
    Checking sqlite v0.32.0
    Checking radicle-crypto v0.11.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-crypto)
    Checking radicle-cob v0.12.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-cob)
    Checking radicle-crdt v0.1.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-crdt)
    Checking radicle v0.13.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle)
    Checking radicle-cli-test v0.10.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-cli-test)
    Checking radicle-fetch v0.10.0 (/srv/http/071309c7-ec2b-4f4f-a6df-a17a61cc80c4/src/radicle-fetch)
error: all variants have the same prefix: `File`
   --> radicle-cli/src/commands/patch/review/builder.rs:140:1
    |
140 | / pub enum ReviewItem {
141 | |     FileAdded {
142 | |         path: PathBuf,
143 | |         header: FileHeader,
...   |
178 | |     },
179 | | }
    | |_^
    |
    = help: remove the prefixes and use full paths to the variants instead of glob imports
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names
    = note: `-D clippy::enum-variant-names` implied by `-D clippy::all`
    = help: to override `-D clippy::all` add `#[allow(clippy::enum_variant_names)]`

error: very complex type used. Consider factoring parts into `type` definitions
   --> radicle-cli/src/commands/patch/review/builder.rs:195:24
    |
195 |     fn paths(&self) -> (Option<(&Path, Oid)>, Option<(&Path, Oid)>) {
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#type_complexity
    = note: `-D clippy::type-complexity` implied by `-D clippy::all`
    = help: to override `-D clippy::all` add `#[allow(clippy::type_complexity)]`

error: could not compile `radicle-cli` (lib) due to 2 previous errors
warning: build failed, waiting for other jobs to finish...