pull repos
This commit is contained in:
@@ -11,5 +11,6 @@ octopush_core = { path = "../octopush_core" }
|
||||
|
||||
eyre = { workspace = true }
|
||||
tracing = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
|
||||
clap = { version = "4.0.18" }
|
||||
|
@@ -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(())
|
||||
|
@@ -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"
|
||||
|
@@ -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(())
|
||||
}
|
||||
|
@@ -1,2 +1,3 @@
|
||||
pub mod git;
|
||||
pub mod storage;
|
||||
pub mod schema;
|
||||
|
2
crates/octopush_core/src/schema/mod.rs
Normal file
2
crates/octopush_core/src/schema/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod models;
|
||||
pub mod parser;
|
26
crates/octopush_core/src/schema/models.rs
Normal file
26
crates/octopush_core/src/schema/models.rs
Normal 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>,
|
||||
},
|
||||
}
|
69
crates/octopush_core/src/schema/parser.rs
Normal file
69
crates/octopush_core/src/schema/parser.rs
Normal 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()
|
||||
}]
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
@@ -12,6 +12,7 @@ pub trait StorageEngine {
|
||||
|
||||
pub type DynStorageEngine = Arc<dyn StorageEngine + Send + Sync>;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct TemporaryDir {
|
||||
path: PathBuf,
|
||||
}
|
||||
|
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user