Rewrite rust #38

Merged
kjuulh merged 24 commits from experiment/local into v0.3 2022-11-27 12:21:36 +01:00
15 changed files with 231 additions and 16 deletions
Showing only changes of commit 1879ad00ee - Show all commits

6
' Normal file
View File

@ -0,0 +1,6 @@
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
#[serde(tag = "apiVersion")]
pub enum Schema {}

53
Cargo.lock generated
View File

@ -124,6 +124,12 @@ dependencies = [
"url",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "hermit-abi"
version = "0.1.19"
@ -155,6 +161,16 @@ version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683"
[[package]]
name = "indexmap"
version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.4"
@ -307,6 +323,7 @@ dependencies = [
"eyre",
"octopush_core",
"octopush_infra",
"tokio",
"tracing",
]
@ -319,7 +336,10 @@ dependencies = [
"git2",
"hex",
"rand",
"serde",
"serde_yaml",
"tokio",
"tracing",
]
[[package]]
@ -512,6 +532,20 @@ name = "serde"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.147"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "serde_json"
@ -524,6 +558,19 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.9.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d232d893b10de3eb7258ff01974d6ee20663d8e833263c99409d4b13a0209da"
dependencies = [
"indexmap",
"itoa",
"ryu",
"serde",
"unsafe-libyaml",
]
[[package]]
name = "sharded-slab"
version = "0.1.4"
@ -736,6 +783,12 @@ dependencies = [
"tinyvec",
]
[[package]]
name = "unsafe-libyaml"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68"
[[package]]
name = "url"
version = "2.3.1"

View File

@ -34,10 +34,10 @@ Refer to [roadmap.md](roadmap.md)
## Installation
Octopush comes in two modes. Client or Client -> Server. Octopush can stand alone as
a client, for smaller and less secure changes. However, for organisations, it
may be useful to use Octopush in server mode, which supports more features, and
has extra security built in.
Octopush comes in two modes. Client or Client -> Server. Octopush can stand
alone as a client, for smaller and less secure changes. However, for
organisations, it may be useful to use Octopush in server mode, which supports
more features, and has extra security built in.
### Client (CLI)
@ -182,8 +182,8 @@ To run the script use
octopush process --path "write-a-readme"
```
This will cause the octopush process to automatically apply the action on the repo
and open a pr.
This will cause the octopush process to automatically apply the action on the
repo and open a pr.
### Query repositories
@ -212,8 +212,8 @@ to help test locally, as well as not cause serious issues. The server
configuration is pretty much the same, except the command would look like so:
`octopush server process --path "write-a-readme" --apply`. Octopush will try to
infer as much as possible, but it may be needed to apply some extra flags to
specify upstream repositories and such. Octopush will also help you setup keys and
such on the first run, using `octopush setup` or `octopush server setup`.
specify upstream repositories and such. Octopush will also help you setup keys
and such on the first run, using `octopush setup` or `octopush server setup`.
## Contributing

View File

@ -1,4 +1,4 @@
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
apiVersion: action
name: write-a-readme
select:
repositories:

View File

@ -1,4 +1,4 @@
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
apiVersion: action
name: write-a-readme
select:
repositories:

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,
}
}