Radish alpha
r
Radicle CI broker
Radicle
Git (anonymous pull)
Log in to clone via SSH
feat: allow adapter to report an optional URL for a run
Lars Wirzenius committed 2 years ago
commit fb51ce2bd97464467f54a0518fc288aa622e1908
parent e529ffbb5c866e2270bc89dd343023251c36fdc6
6 files changed +143 -8
modified ci-broker.md
@@ -15,10 +15,10 @@ run test code as part of running `cargo test`.
~~~{#broker.yaml .file .yaml}
db: ci-broker.db
report_dir: reports
-
default_adapter: dummy
+
default_adapter: mcadapterface
adapters:
-
  dummy:
-
    command: ./dummy.sh
+
  mcadapterface:
+
    command: ./adapter.sh
    env:
      RADICLE_NATIVE_CI: native-ci.yaml
    sensitive_env: {}
@@ -155,8 +155,8 @@ when I run synthetic-events synt.sock trigger.json shutdown.json --log log.txt
given an installed ci-broker
given a directory reports
given file broker.yaml
-
given file dummy.sh
-
when I run chmod +x dummy.sh
+
given file adapter.sh from dummy.sh
+
when I run chmod +x adapter.sh

when I run sed -i 's/"auto"/false/' homedir/.radicle/config.json
when I try to run env HOME=homedir RAD_HOME=homedir/.radicle RAD_SOCKET=synt.sock RUST_LOG=debug ci-broker broker.yaml
@@ -165,6 +165,64 @@ then file reports/index.html contains "xyzzy"
~~~


+
## Adapter can provide URL for info on run
+

+
_Requirement:_ The adapter can provide a URL for information about the
+
run, such a run log. This optional.
+

+
_Justification:_ The CI broker does not itself store the run log, but
+
it's useful to be able to point users at one. The CI broker can put
+
that into a Radicle COB or otherwise store it so that users can see
+
it. Note, however, that the adapter gets to decide which URL to
+
provide: it need not be the run log. It might, for example, be a URL
+
to the web view of a "pipeline" in GitLab CI instead, from which the
+
user can access individual logs.
+

+
_Stakeholder:_ Lars.
+

+
~~~scenario
+
given a directory homedir
+
when I run env HOME=homedir RAD_PASSPHRASE= RAD_HOME=homedir/.radicle rad auth --alias brokertest
+

+
when I run env HOME=homedir git config --global user.email radicle@example.com
+
when I run env HOME=homedir git config --global user.name TestyMcTestFace
+
when I run env HOME=homedir git init testy
+
given file testy/test.txt from adapter-with-url.sh
+

+
when I run, in testy, env HOME=../homedir git add .
+
when I run, in testy, env HOME=../homedir git commit -am test
+
when I run, in testy, env HOME=../homedir git status
+
when I run, in testy, env HOME=../homedir RAD_HOME=../homedir/.radicle RAD_PASSPHRASE= rad init --name testy --description test --default-branch master --private --no-confirm --no-seed
+

+
given an installed synthetic-events
+
given file trigger.json
+
given file shutdown.json
+
given file set-rid
+
when I run env HOME=../homedir python3 set-rid trigger.json testy
+
when I run synthetic-events synt.sock trigger.json shutdown.json --log log.txt
+

+
given an installed ci-broker
+
given a directory reports
+
given file broker.yaml
+
given file adapter.sh from adapter-with-url.sh
+
when I run chmod +x adapter.sh
+

+
when I run sed -i 's/"auto"/false/' homedir/.radicle/config.json
+
when I try to run env HOME=homedir RAD_HOME=homedir/.radicle RAD_SOCKET=synt.sock RUST_LOG=debug ci-broker broker.yaml
+
then command is successful
+

+
when I run cat reports/index.html
+
then file reports/index.html contains "https://ci.example.com/xyzzy"
+
~~~
+

+

+
~~~{#adapter-with-url.sh .file .sh}
+
#!/bin/bash
+
set -euo pipefail
+
echo '{"response":"triggered","run_id":{"id":"xyzzy"},"info_url":"https://ci.example.com/xyzzy"}'
+
echo '{"response":"finished","result":"success"}'
+
~~~
+

## Gives helpful error message if node socket can't be found

_Requirement:_ If the CI broker can't connect to the Radicle node
@@ -300,6 +358,23 @@ when I try to run ./dummy.sh
then command is successful
~~~

+
## Adapter with URL runs successfully
+

+
_Requirement:_ The adapter with a URL (in embedded file
+
`adapter-with-url.sh`) runs successfully.
+

+
_Justification:_ Test scenarios using this adapter need to be able to
+
rely that it works.
+

+
_Stakeholder:_ Lars
+

+
~~~scenario
+
given file adapter-with-url.sh
+
when I run chmod +x adapter-with-url.sh
+
when I try to run ./adapter-with-url.sh
+
then command is successful
+
~~~
+

## Event synthesizer terminates after first connection

_Requirement:_ The event synthesizer runs in the background, but
modified src/adapter.rs
@@ -106,9 +106,12 @@ impl Adapter {
            let resp = Response::from_str(&line).map_err(AdapterError::ParseResponse)?;
            debug!("first response {resp:#?}");
            match resp {
-
                Response::Triggered { run_id } => {
+
                Response::Triggered { run_id, info_url } => {
                    run.set_state(RunState::Running);
                    run.set_adapter_run_id(run_id);
+
                    if let Some(url) = info_url {
+
                        run.set_adapter_info_url(&url);
+
                    }
                    status.push_run(run.clone());
                }
                _ => return Err(AdapterError::NotTriggered(resp)),
modified src/bin/broker-messages.rs
@@ -30,6 +30,15 @@ fn main() {
        serde_json::to_string(&triggered).expect("serialize message")
    );

+
    let triggered = Response::triggered_with_url(
+
        RunId::from("any-string-works-as-run-id"),
+
        "https://ci.example.com/xyzzy",
+
    );
+
    println!(
+
        "Triggered response with URL:\n{}\n",
+
        serde_json::to_string(&triggered).expect("serialize message")
+
    );
+

    let finished = Response::finished(RunResult::Success);
    println!(
        "Successful response:\n{}\n",
modified src/msg.rs
@@ -601,6 +601,9 @@ pub enum Response {
    Triggered {
        /// The identifier for the CI run assigned by the adapter.
        run_id: RunId,
+

+
        /// Optional informational URL for the run.
+
        info_url: Option<String>,
    },

    /// A CI run has finished.
@@ -611,9 +614,20 @@ pub enum Response {
}

impl Response {
-
    /// Create a `Response::Triggered` message.
+
    /// Create a `Response::Triggered` message without an info URL.
    pub fn triggered(run_id: RunId) -> Self {
-
        Self::Triggered { run_id }
+
        Self::Triggered {
+
            run_id,
+
            info_url: None,
+
        }
+
    }
+

+
    /// Create a `Response::Triggered` message with an info URL.
+
    pub fn triggered_with_url(run_id: RunId, url: &str) -> Self {
+
        Self::Triggered {
+
            run_id,
+
            info_url: Some(url.into()),
+
        }
    }

    /// Create a `Response::Finished` message.
modified src/pages.rs
@@ -206,6 +206,16 @@ impl PageData {
                };

                item.push_child(result);
+

+
                if let Some(url) = run.adapter_info_url() {
+
                    item.push_child(Element::new(Tag::Br));
+
                    let log = Element::new(Tag::Span).with_child(
+
                        Element::new(Tag::A)
+
                            .with_attribute("href", url)
+
                            .with_text("log"),
+
                    );
+
                    item.push_child(log);
+
                }
            }

            list.push_child(item);
@@ -342,6 +352,17 @@ impl PageData {
                    .with_text("no log yet")
            };

+
            let info_url = if let Some(url) = run.adapter_info_url() {
+
                Element::new(Tag::Span).with_child(
+
                    Element::new(Tag::A)
+
                        .with_attribute("href", url)
+
                        .with_text("info from adapter"),
+
                )
+
            } else {
+
                Element::new(Tag::Span)
+
                    .with_child(Element::new(Tag::Span).with_text("no info from adapter"))
+
            };
+

            list.push_child(
                Element::new(Tag::Li)
                    .with_text(run.timestamp())
@@ -349,6 +370,7 @@ impl PageData {
                    .with_child(current)
                    .with_text(" ")
                    .with_child(link)
+
                    .with_child(info_url)
                    .with_child(Element::new(Tag::Br))
                    .with_child(Self::whence_as_html(run.whence())),
            );
modified src/run.rs
@@ -10,6 +10,7 @@ use crate::msg::{Revision, RunId, RunResult};
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Run {
    adapter_run_id: Option<RunId>,
+
    adapter_info_url: Option<String>,
    repo_id: RepoId,
    repo_alias: String,
    timestamp: String,
@@ -23,6 +24,7 @@ impl Run {
    pub fn new(repo_id: RepoId, alias: &str, whence: Whence, timestamp: String) -> Self {
        Self {
            adapter_run_id: None,
+
            adapter_info_url: None,
            repo_id,
            repo_alias: alias.into(),
            timestamp,
@@ -62,6 +64,16 @@ impl Run {
        self.adapter_run_id.as_ref()
    }

+
    /// Set the info URL reported by the adapter.
+
    pub fn set_adapter_info_url(&mut self, info_url: &str) {
+
        self.adapter_info_url = Some(info_url.into());
+
    }
+

+
    /// Return the info URL if any.
+
    pub fn adapter_info_url(&self) -> Option<&str> {
+
        self.adapter_info_url.as_deref()
+
    }
+

    /// Return the state of the CI run.
    pub fn state(&self) -> RunState {
        self.state