Rewrite rust #38
6
'
Normal file
6
'
Normal 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
53
Cargo.lock
generated
@ -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"
|
||||
|
16
README.md
16
README.md
@ -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
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
|
||||
apiVersion: action
|
||||
name: write-a-readme
|
||||
select:
|
||||
repositories:
|
||||
|
@ -1,4 +1,4 @@
|
||||
apiVersion: git.front.kjuulh.io/kjuulh/octopush/blob/main/schema/v1
|
||||
apiVersion: action
|
||||
name: write-a-readme
|
||||
select:
|
||||
repositories:
|
||||
|
@ -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,9 +17,31 @@ 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()))
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user