Radish alpha
r
rad:z3qg5TKmN83afz2fj9z3fQjU8vaYE
Radicle CI adapter for native CI
Radicle
Git
feat: return URL to build log if base URL is configured
Lars Wirzenius committed 1 year ago
commit 7947dd6571f9b29a9faf6e4c139a7b4078fd017a
parent 7f6aff9
3 files changed +86 -9
modified src/engine.rs
@@ -202,7 +202,16 @@ impl Engine {
        let storage = profile.storage.path();

        // Write response to indicate run has been triggered.
-
        write_triggered(&run_id)?;
+
        if let Some(url) = &self.config.base_url {
+
            let url = if url.ends_with('/') {
+
                format!("{url}{}/log.html", run_id)
+
            } else {
+
                format!("{url}/{}/log.html", run_id)
+
            };
+
            write_triggered(&run_id, Some(&url))?;
+
        } else {
+
            write_triggered(&run_id, None)?;
+
        }

        // Create and set up the run.
        let mut run = Run::new(&run_dir, &run_log_filename)?;
modified src/msg.rs
@@ -18,10 +18,13 @@ fn write_response(resp: &Response) -> Result<(), NativeMessageError> {
}

/// Write a "triggered" response to stdout.
-
pub fn write_triggered(run_id: &RunId) -> Result<(), NativeMessageError> {
-
    write_response(&Response::triggered(RunId::from(
-
        run_id.to_string().as_str(),
-
    )))?;
+
pub fn write_triggered(run_id: &RunId, info_url: Option<&str>) -> Result<(), NativeMessageError> {
+
    let response = if let Some(url) = info_url {
+
        Response::triggered_with_url(run_id.clone(), url)
+
    } else {
+
        Response::triggered(run_id.clone())
+
    };
+
    write_response(&response)?;
    Ok(())
}

modified tests/integration.rs
@@ -30,7 +30,7 @@ use std::{
use radicle::{git::Oid, identity::Did, prelude::RepoId};
use radicle_ci_broker::msg::{
    Author, EventCommonFields, EventType, Patch, PatchAction, PatchEvent, Repository, Request,
-
    Response, RunResult, State,
+
    Response, RunId, RunResult, State,
};
use tempfile::{tempdir, TempDir};

@@ -68,11 +68,34 @@ impl AdapterResult {
        assert_eq!(self.exit, wanted);
    }

+
    // Return run id from adapter, if one was returned.
+
    fn run_id(&self) -> Option<RunId> {
+
        let msgs = self.messages().unwrap();
+
        assert!(!msgs.is_empty());
+
        match &msgs[0] {
+
            Response::Triggered { run_id, .. } => Some(run_id.clone()),
+
            _ => None,
+
        }
+
    }
+

    // Assert that the adapter sent a message with a run ID.
    fn assert_got_run_id(&self) {
+
        assert!(self.run_id().is_some());
+
    }
+

+
    // Assert that the adapter sent a message the expected info URL.
+
    fn assert_url_is(&self, expected: &str) {
        let msgs = self.messages().unwrap();
        assert!(!msgs.is_empty());
-
        assert!(matches!(msgs[0], Response::Triggered { .. }));
+
        match &msgs[0] {
+
            Response::Triggered {
+
                run_id: _,
+
                info_url: Some(url),
+
            } => {
+
                assert_eq!(url, expected);
+
            }
+
            _ => panic!("unexpected message {:#?}", msgs[0]),
+
        }
    }

    // Assert that the adapter wrote a message indicating the CI run
@@ -209,8 +232,11 @@ enum ConfigKind {
    // No config: file is empty.
    EmptyConfig(PathBuf),

-
    // Valid config.
+
    // Valid config, without info base URL.
    Valid(PathBuf),
+

+
    // Valid, but with info base URL.
+
    ValidWithUrl(PathBuf, String),
}

// What kind of trigger message should we give the adapter?
@@ -277,6 +303,14 @@ impl TestCaseBuilder {
        self
    }

+
    fn with_config_with_url(mut self, url: &str) -> Self {
+
        self.config = Some(ConfigKind::ValidWithUrl(
+
            self.tmp.path().join("config.yaml"),
+
            url.into(),
+
        ));
+
        self
+
    }
+

    fn with_empty_request(mut self) -> Self {
        self.request = Some(TriggerKind::Empty);
        self
@@ -334,6 +368,16 @@ impl TestCaseBuilder {
                state: tmp.join("state"),
                log: tmp.join("log.html"),
                timeout: Some(TIMEOUT),
+
                base_url: None,
+
            };
+
            let config = serde_yaml::to_string(&config).unwrap();
+
            std::fs::write(filename, config)?;
+
        } else if let ConfigKind::ValidWithUrl(filename, url) = &config {
+
            let config = Config {
+
                state: tmp.join("state"),
+
                log: tmp.join("log.html"),
+
                timeout: Some(TIMEOUT),
+
                base_url: Some(url.into()),
            };
            let config = serde_yaml::to_string(&config).unwrap();
            std::fs::write(filename, config)?;
@@ -353,7 +397,8 @@ impl TestCaseBuilder {
            ConfigKind::UnsetEnvVar => (),
            ConfigKind::DoesNotExist(filename)
            | ConfigKind::EmptyConfig(filename)
-
            | ConfigKind::Valid(filename) => {
+
            | ConfigKind::Valid(filename)
+
            | ConfigKind::ValidWithUrl(filename, _) => {
                envs.insert("RADICLE_NATIVE_CI".into(), filename.display().to_string());
            }
        }
@@ -690,3 +735,23 @@ fn happy_path() -> TestResult {
    result.assert_got_success();
    Ok(())
}
+

+
// Does the adapter construct a URL to the build log?
+
#[test]
+
fn happy_path_with_log_url() -> TestResult {
+
    let result = TestCaseBuilder::new()?
+
        .with_config_with_url("https://ci.radicle.liw.fi") // note lack of trailing slash
+
        .with_request()
+
        .with_shell("echo hello, world")
+
        .build()?
+
        .run()?;
+

+
    result.assert_got_exit(0);
+
    result.assert_got_run_id();
+
    result.assert_got_success();
+

+
    let run_id = result.run_id().unwrap();
+
    let expected = format!("https://ci.radicle.liw.fi/{}/log.html", run_id);
+
    result.assert_url_is(&expected);
+
    Ok(())
+
}