pull repos

This commit is contained in:
2022-11-21 21:27:36 +01:00
parent 5fc7d7caf9
commit 1879ad00ee
15 changed files with 231 additions and 16 deletions

View File

@@ -11,5 +11,6 @@ octopush_core = { path = "../octopush_core" }
eyre = { workspace = true }
tracing = { workspace = true }
tokio = { workspace = true }
clap = { version = "4.0.18" }

View File

@@ -1,4 +1,7 @@
use std::path::{self, PathBuf};
use clap::{Arg, ArgAction, ArgMatches, Command};
use octopush_core::schema::{self, models::Action};
use octopush_infra::service_register::ServiceRegister;
pub fn execute_cmd() -> Command {
@@ -16,17 +19,39 @@ pub fn execute_cmd() -> Command {
}
pub async fn execute_subcommand(args: &ArgMatches) -> eyre::Result<()> {
let _action = args
let action = args
.get_one::<String>("action")
.ok_or(eyre::anyhow!("--action is required"))?;
let service_register = ServiceRegister::new();
service_register
.git_provider
.clone_from_url("https://git.front.kjuulh.io/kjuulh/cuddle".to_string())
let action_path: PathBuf = action.into();
let schema = service_register
.schema_parser
.parse_file(action_path.join("octopush.yml"))
.await?;
match schema {
schema::models::Schema::Action {
name,
select,
actions,
} => {
tracing::debug!(name, "running action");
let mut repo_clones = Vec::with_capacity(select.repositories.len());
for repo in select.repositories {
let gp = service_register.git_provider.clone();
repo_clones.push(tokio::spawn(async move { gp.clone_from_url(repo).await }));
}
for repo_clone in repo_clones {
let report = repo_clone.await??;
}
}
}
service_register.cleanup().await?;
Ok(())

View File

@@ -9,7 +9,10 @@ edition = "2021"
async-trait = { workspace = true }
eyre = { workspace = true }
tokio = { workspace = true }
tracing = { workspace = true }
rand = "0.8.5"
hex = "0.4.3"
git2 = "0.15.0"
serde = { version = "1.0.147", features = ["derive"] }
serde_yaml = "0.9.14"

View File

@@ -1,3 +1,5 @@
use git2::{Cred, RemoteCallbacks};
use crate::storage::DynStorageEngine;
use super::GitProvider;
@@ -15,10 +17,32 @@ impl GitHubGitProvider {
#[async_trait::async_trait]
impl GitProvider for GitHubGitProvider {
async fn clone_from_url(&self, url: String) -> eyre::Result<()> {
tracing::debug!(url, "allocating dir");
let dir = self.storage_engine.allocate_dir().await?;
tokio::task::spawn_blocking(move || git2::Repository::clone(url.as_str(), dir.path()))
.await??;
tokio::task::spawn_blocking(move || {
let mut callbacks = RemoteCallbacks::new();
callbacks.credentials(|url, username_from_url, _allowed_types| {
tracing::debug!(username_from_url, url, "pulling key from ssh-agent");
Cred::ssh_key_from_agent(username_from_url.unwrap())
});
let mut fo = git2::FetchOptions::new();
fo.remote_callbacks(callbacks);
let mut builder = git2::build::RepoBuilder::new();
builder.fetch_options(fo);
let path = dir.path();
tracing::debug!(
url,
path = path.as_os_str().to_string_lossy().to_string(),
"clone git repo"
);
builder.clone(url.as_str(), path.as_path())
})
.await??;
Ok(())
}

View File

@@ -1,2 +1,3 @@
pub mod git;
pub mod storage;
pub mod schema;

View File

@@ -0,0 +1,2 @@
pub mod models;
pub mod parser;

View File

@@ -0,0 +1,26 @@
use serde::{Deserialize, Serialize};
pub type Repository = String;
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
pub struct SelectAction {
pub repositories: Vec<Repository>,
}
#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
#[serde(tag = "type")]
pub enum Action {
#[serde(rename = "go")]
Go { entry: String },
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(tag = "apiVersion")]
pub enum Schema {
#[serde(rename = "action")]
Action {
name: String,
select: SelectAction,
actions: Vec<Action>,
},
}

View File

@@ -0,0 +1,69 @@
use std::{path::PathBuf, sync::Arc};
use async_trait::async_trait;
use super::models::Schema;
#[async_trait]
pub trait SchemaParser {
async fn parse_file(&self, file: PathBuf) -> eyre::Result<Schema>;
}
pub type DynSchemaParser = Arc<dyn SchemaParser + Send + Sync>;
#[derive(Debug)]
pub struct DefaultSchemaParser {}
#[async_trait]
impl SchemaParser for DefaultSchemaParser {
async fn parse_file(&self, file: PathBuf) -> eyre::Result<Schema> {
let file = tokio::fs::read(file).await?;
self.parse(file)
}
}
impl DefaultSchemaParser {
pub fn new() -> Self {
Self {}
}
pub fn parse(&self, contents: Vec<u8>) -> eyre::Result<Schema> {
let schema = serde_yaml::from_slice(contents.as_slice())?;
Ok(schema)
}
}
mod test {
use super::DefaultSchemaParser;
use crate::schema::models::{Action, Schema, SelectAction};
#[test]
fn can_parse_action() {
let content = r#"apiVersion: action
name: write-a-readme
select:
repositories:
- git@git.front.kjuulh.io:kjuulh/octopush-test.git
actions:
- type: go
entry: "main.go"
"#;
let res = DefaultSchemaParser::new().parse(content.trim().into());
assert_eq!(
res.unwrap(),
Schema::Action {
name: "write-a-readme".into(),
select: SelectAction {
repositories: vec!["git@git.front.kjuulh.io:kjuulh/octopush-test.git".into()]
},
actions: vec![Action::Go {
entry: "main.go".into()
}]
}
)
}
}

View File

@@ -12,6 +12,7 @@ pub trait StorageEngine {
pub type DynStorageEngine = Arc<dyn StorageEngine + Send + Sync>;
#[derive(Clone, Debug)]
pub struct TemporaryDir {
path: PathBuf,
}

View File

@@ -2,22 +2,26 @@ use std::sync::Arc;
use octopush_core::{
git::{github::GitHubGitProvider, DynGitProvider},
schema::parser::{DefaultSchemaParser, DynSchemaParser},
storage::{local::LocalStorageEngine, DynStorageEngine},
};
pub struct ServiceRegister {
pub storage_engine: DynStorageEngine,
pub git_provider: DynGitProvider,
pub schema_parser: DynSchemaParser,
}
impl ServiceRegister {
pub fn new() -> Self {
let storage_engine = Arc::new(LocalStorageEngine::new("/tmp/octopush".into()));
let git_provider = Arc::new(GitHubGitProvider::new(storage_engine.clone()));
let schema_parser = Arc::new(DefaultSchemaParser::new());
Self {
storage_engine,
git_provider,
schema_parser,
}
}