Compare commits

..

2 Commits

Author SHA1 Message Date
Lol3rrr
00c45ecba5 Initial version 2026-01-17 20:51:09 +01:00
Lol3rrr
d0dddb32e6 Initial setup 2026-01-17 20:50:27 +01:00
7 changed files with 1785 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@@ -0,0 +1,4 @@
target/
runner_state.json
register_token
instance_url

1496
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

2
Cargo.toml Normal file
View File

@@ -0,0 +1,2 @@
[workspace]
members = ["nomact"]

29
nomact/Cargo.toml Normal file
View File

@@ -0,0 +1,29 @@
[package]
name = "nomact"
version = "0.1.0"
edition = "2024"
[dependencies]
tonic = { version = "0.14" }
prost = { version = "0.14" }
tonic-prost = { version = "0.14" }
prost-types = { version = "0.14" }
tonic-web = { version = "0.14" }
hyper = { version = "1.8" }
hyper-util = { version = "0.1" }
hyper-rustls = { version = "0.27" }
rustls = { version = "0.23" }
tower = { version = "0.5" }
http-body = { version = "1.0" }
tokio = { version = "1.49", features = ["rt", "net", "fs"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
serde_yaml = { version = "0.9" }
tracing = { version = "0.1", features = ["attributes"] }
tracing-subscriber = { version = "0.3", features = [] }
[build-dependencies]
tonic-prost-build = { version = "0.14" }

17
nomact/build.rs Normal file
View File

@@ -0,0 +1,17 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_prost_build::configure()
.build_server(false)
.compile_protos(
&["../actions-proto-def/proto/runner/v1/services.proto"],
&["../actions-proto-def/proto"],
)?;
tonic_prost_build::configure()
.build_server(false)
.compile_protos(
&["../actions-proto-def/proto/ping/v1/services.proto"],
&["../actions-proto-def/proto"],
)?;
Ok(())
}

115
nomact/src/lib.rs Normal file
View File

@@ -0,0 +1,115 @@
use std::vec;
pub mod runner {
tonic::include_proto!("runner.v1");
}
pub mod ping {
tonic::include_proto!("ping.v1");
}
pub struct RegisterConfig {
pub name: String,
pub register_token: String,
pub instance: hyper::Uri,
pub version: String,
pub labels: Vec<String>,
pub ephemeral: bool,
}
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct RegisteredRunner {
pub uuid: String,
pub token: String,
}
pub fn instance_to_api_url(instance: hyper::Uri) -> hyper::Uri {
let mut parts = instance.into_parts();
parts.path_and_query = Some("/api/actions".try_into().unwrap());
hyper::Uri::from_parts(parts).unwrap()
}
pub async fn register(conf: RegisterConfig) -> Result<RegisteredRunner, ()> {
use hyper_rustls::ConfigBuilderExt;
let tls = rustls::ClientConfig::builder()
.with_native_roots()
.unwrap()
.with_no_client_auth();
// Prepare the HTTPS connector
let https = hyper_rustls::HttpsConnectorBuilder::new()
.with_tls_config(tls)
.https_or_http()
.enable_http1()
.build();
// Must use hyper directly...
let client = hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new())
.build(https);
let svc = tower::ServiceBuilder::new()
.layer(tonic_web::GrpcWebClientLayer::new())
.service(client);
let uri = instance_to_api_url(conf.instance);
let mut client = runner::runner_service_client::RunnerServiceClient::with_origin(svc, uri);
let request = tonic::Request::new(runner::RegisterRequest {
name: conf.name,
token: conf.register_token,
version: conf.version,
labels: conf.labels,
ephemeral: conf.ephemeral,
..Default::default()
});
let response = client.register(request).await.unwrap();
match response.into_inner().runner {
Some(runner) => Ok(RegisteredRunner {
uuid: runner.uuid,
token: runner.token,
}),
None => Err(()),
}
}
pub async fn run_task<T>(
client: &mut runner::runner_service_client::RunnerServiceClient<T>,
task: runner::Task,
) where
T: tonic::client::GrpcService<tonic::body::Body>,
T::Error: Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
T::ResponseBody: http_body::Body<Data = prost::bytes::Bytes> + std::marker::Send + 'static,
<T::ResponseBody as http_body::Body>::Error:
Into<Box<dyn std::error::Error + Send + Sync + 'static>> + std::marker::Send,
{
tracing::debug!(?task, "Run-Task");
let workflow = core::str::from_utf8(task.workflow_payload()).unwrap();
tracing::info!("Raw-Workflow: {}", workflow);
let task_id = task.id;
let task_workflow = core::str::from_utf8(task.workflow_payload()).unwrap();
let task_context = task.context.as_ref();
let task_secrets = &task.secrets;
let task_needs = &task.needs;
let task_vars = &task.vars;
let workflow = serde_yaml::from_slice::<serde_yaml::Value>(task.workflow_payload());
tracing::info!("Workflow: {:?}", &workflow);
let update_req = tonic::Request::new(runner::UpdateTaskRequest {
outputs: std::collections::HashMap::new(),
state: Some(runner::TaskState {
id: task.id,
result: runner::Result::Failure.into(),
started_at: None,
stopped_at: None,
steps: vec![],
}),
});
let resp = client.update_task(update_req).await.unwrap();
tracing::info!(resp = tracing::field::debug(resp.get_ref()), "Update Task State");
}

122
nomact/src/main.rs Normal file
View File

@@ -0,0 +1,122 @@
use hyper_rustls::ConfigBuilderExt;
use tracing_subscriber::layer::SubscriberExt;
use nomact::runner;
fn main() {
let registry = tracing_subscriber::registry().with(tracing_subscriber::fmt::layer());
tracing::subscriber::set_global_default(registry).unwrap();
tracing::info!("Starting...");
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let register_token = include_str!("../../register_token");
let instance_url = include_str!("../../instance_url");
let name = "Testing";
let runner = rt.block_on(async move {
let path = "./runner_state.json";
match tokio::fs::try_exists(path).await {
Ok(true) => {
tracing::info!("Using already registered runner");
let content = tokio::fs::read(path).await.unwrap();
serde_json::from_slice::<nomact::RegisteredRunner>(&content).unwrap()
}
_ => {
tracing::info!("Registering new runner");
let runner = nomact::register(nomact::RegisterConfig {
name: name.into(),
register_token: register_token.into(),
instance: hyper::Uri::from_static(instance_url),
version: "0.0.1".into(),
labels: Vec::new(),
ephemeral: false,
})
.await
.unwrap();
tracing::info!(?runner, "Registered Runner");
let serialized = serde_json::to_vec(&runner).unwrap();
tokio::fs::write(path, serialized).await.unwrap();
runner
}
}
});
rt.block_on(async move {
let tls = rustls::ClientConfig::builder()
.with_native_roots()
.unwrap()
.with_no_client_auth();
// Prepare the HTTPS connector
let https = hyper_rustls::HttpsConnectorBuilder::new()
.with_tls_config(tls)
.https_or_http()
.enable_http1()
.build();
// Must use hyper directly...
let client =
hyper_util::client::legacy::Client::builder(hyper_util::rt::TokioExecutor::new())
.build(https);
let svc = tower::ServiceBuilder::new()
.layer(tonic_web::GrpcWebClientLayer::new())
.service(client);
let mut client = runner::runner_service_client::RunnerServiceClient::with_origin(
tonic::service::interceptor::InterceptedService::new(
svc,
move |mut req: tonic::Request<()>| {
req.metadata_mut()
.insert("x-runner-uuid", runner.uuid.as_str().try_into().unwrap());
req.metadata_mut()
.insert("x-runner-token", runner.token.as_str().try_into().unwrap());
Ok(req)
},
),
nomact::instance_to_api_url(hyper::Uri::from_static(instance_url))
);
let request = tonic::Request::new(runner::DeclareRequest {
version: "0.0.2".into(),
labels: vec![
"ubuntu-latest".into(),
"ubuntu-22.04".into(),
"ubuntu-20.04".into(),
],
});
let response = client.declare(request).await.unwrap();
tracing::info!(runner = tracing::field::debug(response.get_ref().runner.as_ref()), "Declare current runner state");
let mut task_id = 0;
loop {
tracing::info!("Checking for new Task");
let request = tonic::Request::new(runner::FetchTaskRequest {
tasks_version: task_id,
});
let response = client.fetch_task(request).await.unwrap();
task_id = response.get_ref().tasks_version;
if let Some(task) = response.into_inner().task {
nomact::run_task(&mut client, task).await;
}
tokio::time::sleep(std::time::Duration::from_secs(30)).await;
}
});
}