cosmicus 0.1.9

Cosmicus Client and Server to make the runner better
Documentation
use crate::types::Id;
use coodev_runner::{
  GithubAuthorization, GlobalSecret, OrganizationSecret, RepositorySecret, Secret, Volume,
  Workflow, WorkflowEvent, WorkflowMessage,
};
use derive_builder::Builder;
use serde::{Deserialize, Serialize};
use std::fs;

#[derive(Serialize, Deserialize, Debug, Clone, Builder)]
// public fields
#[builder(setter(into), field(public))]
pub struct RunWorkflowConfig {
  pub id: Id,
  pub workflow: Workflow,
  pub event: WorkflowEvent,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct CancelWorkflowRun {
  pub id: Id,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RegisterRunner {
  pub id: Id,
  pub name: String,
  // pub token: String,
  pub version: String,
  pub max_runs: u64,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
// 不能设置成 untaged,否则会导致无法解析
#[serde(rename_all = "snake_case")]
pub enum RunnerConfigScope {
  Repository(String),
  Organization(String),
  Global,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RunnerVolume {
  pub key: String,
  /// Content of the file
  pub content: String,
  pub scope: RunnerConfigScope,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RunnerSecret {
  pub key: String,
  pub value: String,
  pub scope: RunnerConfigScope,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct RunnerConfig {
  pub github_authorization: GithubAuthorization,
  pub volumes: Vec<RunnerVolume>,
  pub secrets: Vec<RunnerSecret>,
  // pub env: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(untagged)]
pub enum Message {
  RegisterRunner(RegisterRunner),
  WorkflowMessage(WorkflowMessage),
  RunWorkflow(RunWorkflowConfig),
  CancelWorkflowRun(CancelWorkflowRun),
  RunnerConfig(RunnerConfig),
}

impl Into<Secret> for RunnerSecret {
  fn into(self) -> Secret {
    match self.scope {
      RunnerConfigScope::Repository(repo) => Secret::Repository(RepositorySecret {
        key: self.key,
        value: self.value,
        repository: repo,
      }),
      RunnerConfigScope::Organization(org) => Secret::Organization(OrganizationSecret {
        key: self.key,
        value: self.value,
        organization: org,
      }),
      RunnerConfigScope::Global => Secret::Global(GlobalSecret {
        key: self.key,
        value: self.value,
      }),
    }
  }
}

impl From<Secret> for RunnerSecret {
  fn from(secret: Secret) -> Self {
    match secret {
      Secret::Global(secret) => RunnerSecret {
        key: secret.key,
        value: secret.value,
        scope: RunnerConfigScope::Global,
      },
      Secret::Repository(secret) => RunnerSecret {
        key: secret.key,
        value: secret.value,
        scope: RunnerConfigScope::Repository(secret.repository),
      },
      Secret::Organization(secret) => RunnerSecret {
        key: secret.key,
        value: secret.value,
        scope: RunnerConfigScope::Organization(secret.organization),
      },
    }
  }
}

impl TryFrom<Volume> for RunnerVolume {
  type Error = anyhow::Error;

  fn try_from(volume: Volume) -> Result<Self, Self::Error> {
    match volume {
      Volume::Global(volume) => {
        let content = fs::read_to_string(&volume.path)?;
        Ok(RunnerVolume {
          key: volume.key,
          content,
          scope: RunnerConfigScope::Global,
        })
      }
      Volume::Repository(volume) => {
        let content = fs::read_to_string(&volume.path)?;
        Ok(RunnerVolume {
          key: volume.key,
          content,
          scope: RunnerConfigScope::Repository(volume.repository),
        })
      }
      Volume::Organization(volume) => {
        let content = fs::read_to_string(&volume.path)?;
        Ok(RunnerVolume {
          key: volume.key,
          content,
          scope: RunnerConfigScope::Organization(volume.organization),
        })
      }
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;

  #[test]
  fn test_deserialize_workflow_event() {
    let volume = RunnerVolume {
      key: "test".into(),
      content: "test".into(),
      scope: RunnerConfigScope::Global,
    };
    let repo_secret = RunnerSecret {
      key: "test".into(),
      value: "test".into(),
      scope: RunnerConfigScope::Repository("owner/name".into()),
    };
    let org_secret = RunnerSecret {
      key: "test".into(),
      value: "test".into(),
      scope: RunnerConfigScope::Organization("owner".into()),
    };
    let msg = Message::RunnerConfig(RunnerConfig {
      github_authorization: GithubAuthorization::PersonalAccessToken("test".into()),
      volumes: vec![volume.clone()],
      secrets: vec![repo_secret.clone(), org_secret.clone()],
    });

    let json = serde_json::to_string(&msg).unwrap();
    println!("{}", json);
    let msg2: Message = serde_json::from_str(&json).unwrap();

    println!("{:?}", msg2);
  }

  #[test]
  fn convert_secret_to_runner_secret() {
    let secret = Secret::Repository(RepositorySecret {
      key: "test".into(),
      value: "test".into(),
      repository: "owner/name".into(),
    });
    let runner_secret: RunnerSecret = secret.clone().into();
    let secret2: Secret = runner_secret.into();
    assert_eq!(secret, secret2);
  }
}