diff --git a/Cargo.lock b/Cargo.lock index 14f07b6e..78d158ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1582,6 +1582,19 @@ dependencies = [ "wasip3", ] +[[package]] +name = "gitmirror" +version = "0.1.0" +dependencies = [ + "prost", + "tokio", + "tonic", + "tonic-prost", + "tonic-prost-build", + "tracing", + "tracing-subscriber", +] + [[package]] name = "group" version = "0.13.0" @@ -1806,6 +1819,19 @@ dependencies = [ "webpki-roots 1.0.7", ] +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.20" @@ -2880,6 +2906,8 @@ dependencies = [ "prettyplease", "prost", "prost-types", + "pulldown-cmark", + "pulldown-cmark-to-cmark", "regex", "syn", "tempfile", @@ -2974,6 +3002,26 @@ dependencies = [ "prost", ] +[[package]] +name = "pulldown-cmark" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" +dependencies = [ + "bitflags", + "memchr", + "unicase", +] + +[[package]] +name = "pulldown-cmark-to-cmark" +version = "22.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50793def1b900256624a709439404384204a5dc3a6ec580281bfaac35e882e90" +dependencies = [ + "pulldown-cmark", +] + [[package]] name = "quick_cache" version = "0.6.22" @@ -4335,6 +4383,74 @@ version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +[[package]] +name = "tonic" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" +dependencies = [ + "async-trait", + "axum", + "base64", + "bytes", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-timeout", + "hyper-util", + "percent-encoding", + "pin-project", + "socket2", + "sync_wrapper", + "tokio", + "tokio-stream", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tonic-build" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f61875ac5293cf72e6c8cf0158086428c82c37229e98c840878f1706b0322" +dependencies = [ + "prettyplease", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tonic-prost" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" +dependencies = [ + "bytes", + "prost", + "tonic", +] + +[[package]] +name = "tonic-prost-build" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "654e5643eff75d7f8c99197ce1440ed19a3474eada74c12bbac488b2cafdae27" +dependencies = [ + "prettyplease", + "proc-macro2", + "prost-build", + "prost-types", + "quote", + "syn", + "tempfile", + "tonic-build", +] + [[package]] name = "tower" version = "0.5.3" @@ -4343,9 +4459,12 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", + "indexmap", "pin-project-lite", + "slab", "sync_wrapper", "tokio", + "tokio-util", "tower-layer", "tower-service", "tracing", @@ -4559,6 +4678,12 @@ dependencies = [ "syn", ] +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" diff --git a/Cargo.toml b/Cargo.toml index c21e2de8..99f7bccd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [workspace] resolver = "2" -members = ["bobbin/crates/*", "shuttle"] +members = ["bobbin/crates/*", "shuttle", "gitmirror"] exclude = ["sites"] [workspace.package] @@ -85,6 +85,11 @@ tantivy = "0.26" tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json"] } +tonic = "0.14" +tonic-prost = "0.14" +tonic-prost-build = "0.14" +prost = "0.14" + thiserror = "2" confique = { version = "0.4", default-features = false, features = ["toml"] } diff --git a/flake.nix b/flake.nix index bdcae9d2..483bc8e5 100644 --- a/flake.nix +++ b/flake.nix @@ -352,6 +352,7 @@ pkgs.protoc-gen-prost-crate pkgs.protoc-gen-prost-serde pkgs.protoc-gen-go + pkgs.protoc-gen-go-grpc (fenix.packages.${system}.combine [ fenix.packages.${system}.stable.cargo fenix.packages.${system}.stable.rustc diff --git a/gitmirror/Cargo.toml b/gitmirror/Cargo.toml new file mode 100644 index 00000000..0a72780a --- /dev/null +++ b/gitmirror/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "gitmirror" +version = "0.1.0" +edition.workspace = true +license.workspace = true +rust-version.workspace = true + +[dependencies] +tonic = { workspace = true } +tonic-prost = { workspace = true } +prost = { workspace = true } +tokio = { workspace = true, features = ["net"] } +tracing = "0.1" +tracing-subscriber = { version = "0.3", features = ["env-filter"] } + +[build-dependencies] +tonic-prost-build = { workspace = true } + +[lints] +workspace = true diff --git a/gitmirror/buf.gen.yaml b/gitmirror/buf.gen.yaml new file mode 100644 index 00000000..fc9cd944 --- /dev/null +++ b/gitmirror/buf.gen.yaml @@ -0,0 +1,12 @@ +version: v2 +plugins: + - local: protoc-gen-go + out: . + opt: + - paths=import + - module=tangled.org/core + - local: protoc-gen-go-grpc + out: . + opt: + - paths=import + - module=tangled.org/core diff --git a/gitmirror/buf.yaml b/gitmirror/buf.yaml new file mode 100644 index 00000000..ba5ddf50 --- /dev/null +++ b/gitmirror/buf.yaml @@ -0,0 +1,3 @@ +version: v2 +modules: + - path: proto diff --git a/gitmirror/build.rs b/gitmirror/build.rs new file mode 100644 index 00000000..f5b16bc6 --- /dev/null +++ b/gitmirror/build.rs @@ -0,0 +1,4 @@ +fn main() -> Result<(), Box> { + tonic_prost_build::compile_protos("proto/gitmirror/v1/gitmirror.proto")?; + Ok(()) +} diff --git a/gitmirror/example/client.go b/gitmirror/example/client.go new file mode 100644 index 00000000..93f584b2 --- /dev/null +++ b/gitmirror/example/client.go @@ -0,0 +1,42 @@ +//go:build ignore + +// Example: how appview will call gitmirror's GitMirrorService. +// +// This file is build-ignored so it never breaks `go build ./...`. It is a +// copy-ready reference for wiring the client into appview once the Go stubs +// are generated (see gitmirror/buf.gen.yaml). +package main + +import ( + "context" + "log" + "time" + + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + + gitmirrorv1 "tangled.org/core/gitmirror/proto/gen" +) + +func main() { + conn, err := grpc.NewClient("127.0.0.1:9000", + grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + log.Fatal(err) + } + defer conn.Close() + + client := gitmirrorv1.NewGitMirrorServiceClient(conn) + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // appview converts its syntax.DID to a string via did.String(). + resp, err := client.Commits(ctx, &gitmirrorv1.CommitsRequest{ + Repo: "did:plc:example", + Options: &gitmirrorv1.CommitsOptions{}, + }) + if err != nil { + log.Fatal(err) + } + log.Println(resp.GetCommits()) +} diff --git a/gitmirror/proto/gitmirror/v1/gitmirror.proto b/gitmirror/proto/gitmirror/v1/gitmirror.proto new file mode 100644 index 00000000..4a1affc7 --- /dev/null +++ b/gitmirror/proto/gitmirror/v1/gitmirror.proto @@ -0,0 +1,25 @@ +syntax = "proto3"; + +package gitmirror.v1; + +option go_package = "tangled.org/core/gitmirror/proto/gen;gitmirrorv1"; + +service GitMirrorService { + rpc Commits(CommitsRequest) returns (CommitsResponse); +} + +message CommitsRequest { + // repo carries the DID as a string; parsing/validation is an impl detail. + string repo = 1; + CommitsOptions options = 2; +} + +message CommitsOptions { + // TODO: fields for the Go CommitsOptions (e.g. ref/branch, limit, cursor) — + // unspecified for now. +} + +message CommitsResponse { + // maps the Go []string return. + repeated string commits = 1; +} diff --git a/gitmirror/src/main.rs b/gitmirror/src/main.rs new file mode 100644 index 00000000..5e433741 --- /dev/null +++ b/gitmirror/src/main.rs @@ -0,0 +1,52 @@ +use std::env; +use std::net::SocketAddr; + +use tonic::transport::Server; +use tonic::{Request, Response, Status}; +use tracing::info; + +pub mod gitmirror { + pub mod v1 { + tonic::include_proto!("gitmirror.v1"); + } +} + +use gitmirror::v1::git_mirror_service_server::{GitMirrorService, GitMirrorServiceServer}; +use gitmirror::v1::{CommitsRequest, CommitsResponse}; + +#[derive(Default)] +struct GitMirror {} + +#[tonic::async_trait] +impl GitMirrorService for GitMirror { + async fn commits( + &self, + _request: Request, + ) -> Result, Status> { + Err(Status::unimplemented("not implemented")) + } +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + tracing_subscriber::fmt() + .with_env_filter( + tracing_subscriber::EnvFilter::try_from_default_env() + .unwrap_or_else(|_| "info".into()), + ) + .init(); + + let addr: SocketAddr = env::var("GITMIRROR_ADDR") + .unwrap_or_else(|_| "127.0.0.1:9000".to_string()) + .parse()?; + + let service = GitMirror::default(); + info!(%addr, "gitmirror gRPC server listening"); + + Server::builder() + .add_service(GitMirrorServiceServer::new(service)) + .serve(addr) + .await?; + + Ok(()) +}