Log from Radicle native CI

Table of contents

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:z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM",
    "alias": "fintohaps"
  },
  "before": "c2863c08ab58e1098ce29ab99d6d3f902e42c8d5",
  "after": "c2863c08ab58e1098ce29ab99d6d3f902e42c8d5",
  "branch": "master",
  "commits": [
    "c2863c08ab58e1098ce29ab99d6d3f902e42c8d5"
  ]
}

.radicle/native.yaml

shell: |
  cargo --version
  rustc --version

  cargo fmt --check
  cargo clippy --all-targets --workspace -- --deny warnings
  cargo build --all-targets --workspace
  cargo doc --workspace --no-deps
  cargo test --workspace --no-fail-fast

Run: git clone /home/_rad/.radicle/storage/z3gqcJUoA1n9HaHKufZs5FCSGazv5 /srv/http/dae80b08-fba5-4655-abe7-77e52a6b4f34/src

Command arguments:

In directory: /

Exit code: 0

Output (stdout and stderr):

Cloning into '/srv/http/dae80b08-fba5-4655-abe7-77e52a6b4f34/src'...
done.

Run: git config advice.detachedHead false

Command arguments:

In directory: /srv/http/dae80b08-fba5-4655-abe7-77e52a6b4f34/src

Exit code: 0

Run: git checkout c2863c08ab58e1098ce29ab99d6d3f902e42c8d5

Command arguments:

In directory: /srv/http/dae80b08-fba5-4655-abe7-77e52a6b4f34/src

Exit code: 0

Output (stdout and stderr):

HEAD is now at c2863c08ab5 radicle: extend emoji support

Run: git show c2863c08ab58e1098ce29ab99d6d3f902e42c8d5

Command arguments:

In directory: /srv/http/dae80b08-fba5-4655-abe7-77e52a6b4f34/src

Exit code: 0

Output (stdout and stderr):

commit c2863c08ab58e1098ce29ab99d6d3f902e42c8d5
Author: Fintan Halpenny <fintan.halpenny@gmail.com>
Date:   Thu Feb 13 10:05:44 2025 +0000

    radicle: extend emoji support
    
    Extend the ranges of emojis that are supported, listed
    [below](#new-emojis-supported).
    
    To ensure that single `char` emojis are supported, the `emojis` crate
    is added as a dev dependency, using it to check that a `Reaction` can
    be constructed for the two groups `SmileysAndEmotion` and
    `PeopleAndBody`.
    
    ### New Emojis Supported
    
    '🩵' 0x1FA75
    '🩶' 0x1FA76
    '🩷' 0x1FA77
    '🫀' 0x1FAC0
    '🫁' 0x1FAC1
    '🫂' 0x1FAC2
    '🫃' 0x1FAC3
    '🫄' 0x1FAC4
    '🫅' 0x1FAC5
    '🫆' 0x1FAC6
    '🫠' 0x1FAE0
    '🫡' 0x1FAE1
    '🫢' 0x1FAE2
    '🫣' 0x1FAE3
    '🫤' 0x1FAE4
    '🫥' 0x1FAE5
    '🫦' 0x1FAE6
    '🫨' 0x1FAE8
    '🫩' 0x1FAE9
    '🫰' 0x1FAF0
    '🫱' 0x1FAF1
    '🫲' 0x1FAF2
    '🫳' 0x1FAF3
    '🫴' 0x1FAF4
    '🫵' 0x1FAF5
    '🫶' 0x1FAF6
    '🫷' 0x1FAF7
    '🫸' 0x1FAF8

diff --git a/Cargo.lock b/Cargo.lock
index 290039d4105..4b4fb944d0e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -638,6 +638,15 @@ dependencies = [
  "zeroize",
 ]
 
+[[package]]
+name = "emojis"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99e1f1df1f181f2539bac8bf027d31ca5ffbf9e559e3f2d09413b9107b5c02f4"
+dependencies = [
+ "phf",
+]
+
 [[package]]
 name = "equivalent"
 version = "1.0.1"
@@ -1709,6 +1718,24 @@ version = "2.3.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
+[[package]]
+name = "phf"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
+dependencies = [
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
+dependencies = [
+ "siphasher 1.0.1",
+]
+
 [[package]]
 name = "pkcs1"
 version = "0.7.5"
@@ -1877,6 +1904,7 @@ dependencies = [
  "colored",
  "crossbeam-channel",
  "cyphernet",
+ "emojis",
  "fastrand",
  "git2",
  "libc",
diff --git a/radicle/Cargo.toml b/radicle/Cargo.toml
index fbc5db7c9ab..74853a1b635 100644
--- a/radicle/Cargo.toml
+++ b/radicle/Cargo.toml
@@ -69,6 +69,7 @@ default-features = false
 optional = true
 
 [dev-dependencies]
+emojis = { version = "0.6" }
 pretty_assertions = { version = "1.3.0" }
 qcheck-macros = { version = "1", default-features = false }
 qcheck = { version = "1", default-features = false }
diff --git a/radicle/src/cob/common.rs b/radicle/src/cob/common.rs
index 9c7eec6801e..bdc94d30035 100644
--- a/radicle/src/cob/common.rs
+++ b/radicle/src/cob/common.rs
@@ -91,12 +91,18 @@ impl Reaction {
     pub fn new(emoji: char) -> Result<Self, ReactionError> {
         let val = emoji as u32;
         let emoticons = 0x1F600..=0x1F64F;
+        let hearts = 0x1FA75..=0x1FA77;
+        let body_update = 0x1FAC0..=0x1FAC6;
+        let emoticons_update = 0x1FAE0..=0x1FAF8;
         let misc = 0x1F300..=0x1F5FF; // Miscellaneous Symbols and Pictographs
         let dingbats = 0x2700..=0x27BF;
         let supp = 0x1F900..=0x1F9FF; // Supplemental Symbols and Pictographs
         let transport = 0x1F680..=0x1F6FF;
 
         if emoticons.contains(&val)
+            || hearts.contains(&val)
+            || body_update.contains(&val)
+            || emoticons_update.contains(&val)
             || misc.contains(&val)
             || dingbats.contains(&val)
             || supp.contains(&val)
@@ -367,32 +373,6 @@ impl From<bool> for Authorization {
     }
 }
 
-#[cfg(test)]
-#[allow(clippy::unwrap_used)]
-mod test {
-    use super::*;
-
-    #[test]
-    fn test_color() {
-        let c = Color::from_str("#ffccaa").unwrap();
-        assert_eq!(c.to_string(), "#ffccaa".to_owned());
-        assert_eq!(serde_json::to_string(&c).unwrap(), "\"#ffccaa\"".to_owned());
-        assert_eq!(serde_json::from_str::<'_, Color>("\"#ffccaa\"").unwrap(), c);
-
-        let c = Color::from_str("#0000aa").unwrap();
-        assert_eq!(c.to_string(), "#0000aa".to_owned());
-
-        let c = Color::from_str("#aa0000").unwrap();
-        assert_eq!(c.to_string(), "#aa0000".to_owned());
-
-        let c = Color::from_str("#00aa00").unwrap();
-        assert_eq!(c.to_string(), "#00aa00".to_owned());
-
-        Color::from_str("#aa00").unwrap_err();
-        Color::from_str("#abc").unwrap_err();
-    }
-}
-
 /// Describes a code location that can be used for comments on
 /// patches, issues, and diffs.
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
@@ -446,3 +426,60 @@ impl std::cmp::Ord for CodeRange {
         }
     }
 }
+
+#[cfg(test)]
+#[allow(clippy::unwrap_used)]
+mod test {
+    use std::collections::BTreeSet;
+
+    use super::*;
+
+    #[test]
+    fn test_color() {
+        let c = Color::from_str("#ffccaa").unwrap();
+        assert_eq!(c.to_string(), "#ffccaa".to_owned());
+        assert_eq!(serde_json::to_string(&c).unwrap(), "\"#ffccaa\"".to_owned());
+        assert_eq!(serde_json::from_str::<'_, Color>("\"#ffccaa\"").unwrap(), c);
+
+        let c = Color::from_str("#0000aa").unwrap();
+        assert_eq!(c.to_string(), "#0000aa".to_owned());
+
+        let c = Color::from_str("#aa0000").unwrap();
+        assert_eq!(c.to_string(), "#aa0000".to_owned());
+
+        let c = Color::from_str("#00aa00").unwrap();
+        assert_eq!(c.to_string(), "#00aa00".to_owned());
+
+        Color::from_str("#aa00").unwrap_err();
+        Color::from_str("#abc").unwrap_err();
+    }
+
+    #[test]
+    fn test_emojis() {
+        let emojis = emojis::Group::SmileysAndEmotion
+            .emojis()
+            .chain(emojis::Group::PeopleAndBody.emojis())
+            .filter_map(|emoji| {
+                if emoji.as_str().chars().count() == 1 {
+                    Some(emoji.as_str().chars().next().unwrap())
+                } else {
+                    None
+                }
+            });
+        let mut failed = BTreeSet::new();
+        for emoji in emojis {
+            if Reaction::new(emoji).is_err() {
+                failed.insert(emoji);
+            }
+        }
+        if !failed.is_empty() {
+            for emoji in failed {
+                eprintln!(
+                    "cannot construct Reaction for '{emoji}' {:#04X}",
+                    (emoji as u32)
+                );
+            }
+            panic!("Failed to construct Reaction for these emojis");
+        }
+    }
+}

Run: bash -c set -xeuo pipefail cargo --version rustc --version cargo fmt --check cargo clippy --all-targets --workspace -- --deny warnings cargo build --all-targets --workspace cargo doc --workspace --no-deps cargo test --workspace --no-fail-fast

Command arguments:

In directory: /srv/http/dae80b08-fba5-4655-abe7-77e52a6b4f34/src

Exit code: 1

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
Diff in /srv/http/dae80b08-fba5-4655-abe7-77e52a6b4f34/src/radicle-cli/src/commands/help.rs at line 91:
     term::blank();
 
     term::print("Do you have feedback?");
-    term::print(" - Chat <\x1b]8;;https://radicle.zulipchat.com\x1b\\radicle.zulipchat.com\x1b]8;;\x1b\\>");
-    term::print(" - Mail <\x1b]8;;mailto:feedback@radicle.xyz\x1b\\feedback@radicle.xyz\x1b]8;;\x1b\\>");
+    term::print(
+        " - Chat <\x1b]8;;https://radicle.zulipchat.com\x1b\\radicle.zulipchat.com\x1b]8;;\x1b\\>",
+    );
+    term::print(
+        " - Mail <\x1b]8;;mailto:feedback@radicle.xyz\x1b\\feedback@radicle.xyz\x1b]8;;\x1b\\>",
+    );
     term::print("   (Messages are automatically posted to the public #feedback channel on Zulip.)");
 
     Ok(())