Skip to content

Commit f8b942d

Browse files
committed
Set up release-plz
1 parent e087e60 commit f8b942d

File tree

13 files changed

+257
-221
lines changed

13 files changed

+257
-221
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: Create release PR
2+
3+
permissions:
4+
pull-requests: write
5+
contents: write
6+
7+
on: workflow_dispatch
8+
9+
jobs:
10+
11+
# Create a PR with the new versions and changelog, preparing the next release. When merged to main,
12+
# the publish-release.yml workflow will automatically publish any Rust package versions.
13+
create-release-pr:
14+
name: Create release PR
15+
runs-on: ubuntu-latest
16+
permissions:
17+
contents: write
18+
pull-requests: write
19+
concurrency: # Don't run overlapping instances of this workflow
20+
group: release-plz-${{ github.ref }}
21+
cancel-in-progress: false
22+
steps:
23+
- name: Checkout repository
24+
uses: actions/checkout@v4
25+
with:
26+
fetch-depth: 0
27+
- name: Install Rust toolchain
28+
uses: dtolnay/rust-toolchain@stable
29+
- name: Run release-plz
30+
uses: release-plz/action@v0.5
31+
with:
32+
command: release-pr
33+
env:
34+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
35+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

.github/workflows/publish-release.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Release and unpublished twirp/twirp-build packages
2+
3+
permissions:
4+
contents: write
5+
6+
on:
7+
push:
8+
branches:
9+
- main
10+
11+
jobs:
12+
13+
# Release any unpublished packages
14+
release-plz-release:
15+
name: Release-plz release
16+
runs-on: ubuntu-latest
17+
permissions:
18+
contents: write
19+
steps:
20+
- name: Checkout repository
21+
uses: actions/checkout@v4
22+
with:
23+
fetch-depth: 0
24+
- name: Install Rust toolchain
25+
uses: dtolnay/rust-toolchain@stable
26+
- name: Run release-plz
27+
uses: release-plz/action@v0.5
28+
with:
29+
command: release
30+
env:
31+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
32+
CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }}

CONTRIBUTING.md

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -41,22 +41,15 @@ cargo build && cargo test
4141
Run clippy and fix any lints:
4242

4343
```sh
44-
cargo fmt --all -- --check
45-
cargo clippy -- --deny warnings -D clippy::unwrap_used
46-
cargo clippy --tests -- --deny warnings -A clippy::unwrap_used
44+
make lint
4745
```
4846

49-
## Releasing (write access required)
47+
## Releasing
5048

51-
If you are one of the maintainers of this package then follow this process:
52-
53-
1. Create a PR for this release with following changes:
54-
- Updated `CHANGELOG.md` with desired change comments and ensure that it has the version to be released with date at the top.
55-
- Go through all recent PRs and make sure they are properly accounted for.
56-
- Make sure all changelog entries have links back to their PR(s) if appropriate.
57-
- Update package version in Cargo.toml.
58-
1. Get an approval and merge your PR.
59-
1. Run ./script/publish from the `main` branch supplying your token and version information.
49+
1. Go to the `Create Release PR` action and press the button to run the action. This will use `release-plz` to create a new release PR.
50+
1. Adjust the generated changelog and version number(s) as necessary.
51+
1. Get PR approval
52+
1. Merge the PR. The `publish-release.yml` workflow will automatically publish a new release of any crate whose version has changed.
6053

6154
## Resources
6255

Cargo.toml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
[workspace]
22

3-
members = [
4-
"crates/*",
5-
"example",
6-
]
3+
members = ["crates/*", "example"]
74
resolver = "2"

README.md

Lines changed: 3 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,6 @@
11
# twirp-rs
22

3-
[Twirp is an RPC protocol](https://twitchtv.github.io/twirp/docs/spec_v7.html) based on HTTP and Protocol Buffers (proto). The protocol uses HTTP URLs to specify the RPC endpoints, and sends/receives proto messages as HTTP request/response bodies. Services are defined in a [.proto file](https://developers.google.com/protocol-buffers/docs/proto3), allowing easy implementation of RPC services with auto-generated clients and servers in different languages.
3+
This repository contains the following crates published to crates.io. Please see their respective README files for more information.
44

5-
The [canonical implementation](https://github.com/twitchtv/twirp) is in Go, this is a Rust implementation of the protocol. Rust protocol buffer support is provided by the [`prost`](https://github.com/tokio-rs/prost) ecosystem.
6-
7-
Unlike [`prost-twirp`](https://github.com/sourcefrog/prost-twirp), the generated traits for serving and accessing RPCs are implemented atop `async` functions. Because traits containing `async` functions [are not directly supported](https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/) in Rust versions prior to 1.75, this crate uses the [`async_trait`](https://github.com/dtolnay/async-trait) macro to encapsulate the scaffolding required to make them work.
8-
9-
## Usage
10-
11-
See the [example](./example) for a complete example project.
12-
13-
Define services and messages in a `.proto` file:
14-
15-
```proto
16-
// service.proto
17-
package service.haberdash.v1;
18-
19-
service HaberdasherAPI {
20-
rpc MakeHat(MakeHatRequest) returns (MakeHatResponse);
21-
}
22-
message MakeHatRequest { }
23-
message MakeHatResponse { }
24-
```
25-
26-
Add the `twirp-build` crate as a build dependency in your `Cargo.toml` (you'll need `prost-build` too):
27-
28-
```toml
29-
# Cargo.toml
30-
[build-dependencies]
31-
twirp-build = "0.3"
32-
prost-build = "0.13"
33-
```
34-
35-
Add a `build.rs` file to your project to compile the protos and generate Rust code:
36-
37-
```rust
38-
fn main() {
39-
let proto_source_files = ["./service.proto"];
40-
41-
// Tell Cargo to rerun this build script if any of the proto files change
42-
for entry in &proto_source_files {
43-
println!("cargo:rerun-if-changed={}", entry);
44-
}
45-
46-
prost_build::Config::new()
47-
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]") // enable support for JSON encoding
48-
.service_generator(twirp_build::service_generator())
49-
.compile_protos(&proto_source_files, &["./"])
50-
.expect("error compiling protos");
51-
}
52-
```
53-
54-
This generates code that you can find in `target/build/your-project-*/out/example.service.rs`. In order to use this code, you'll need to implement the trait for the proto defined service and wire up the service handlers to a hyper web server. See [the example `main.rs`]( example/src/main.rs) for details.
55-
56-
Include the generated code, create a router, register your service, and then serve those routes in the hyper server:
57-
58-
```rust
59-
mod haberdash {
60-
include!(concat!(env!("OUT_DIR"), "/service.haberdash.v1.rs"));
61-
}
62-
63-
use axum::Router;
64-
use haberdash::{MakeHatRequest, MakeHatResponse};
65-
66-
#[tokio::main]
67-
pub async fn main() {
68-
let api_impl = Arc::new(HaberdasherApiServer {});
69-
let twirp_routes = Router::new()
70-
.nest(haberdash::SERVICE_FQN, haberdash::router(api_impl));
71-
let app = Router::new()
72-
.nest("/twirp", twirp_routes)
73-
.fallback(twirp::server::not_found_handler);
74-
75-
let tcp_listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
76-
if let Err(e) = axum::serve(tcp_listener, app).await {
77-
eprintln!("server error: {}", e);
78-
}
79-
}
80-
81-
// Define the server and implement the trait.
82-
struct HaberdasherApiServer;
83-
84-
#[async_trait]
85-
impl haberdash::HaberdasherApi for HaberdasherApiServer {
86-
async fn make_hat(&self, ctx: twirp::Context, req: MakeHatRequest) -> Result<MakeHatResponse, TwirpErrorResponse> {
87-
todo!()
88-
}
89-
}
90-
```
91-
92-
This code creates an `axum::Router`, then hands it off to `axum::serve()` to handle networking.
93-
This use of `axum::serve` is optional. After building `app`, you can instead invoke it from any
94-
`hyper`-based server by importing `twirp::tower::Service` and doing `app.call(request).await`.
95-
96-
## Usage (client side)
97-
98-
On the client side, you also get a generated twirp client (based on the rpc endpoints in your proto). Include the generated code, create a client, and start making rpc calls:
99-
100-
``` rust
101-
mod haberdash {
102-
include!(concat!(env!("OUT_DIR"), "/service.haberdash.v1.rs"));
103-
}
104-
105-
use haberdash::{HaberdasherApiClient, MakeHatRequest, MakeHatResponse};
106-
107-
#[tokio::main]
108-
pub async fn main() {
109-
let client = Client::from_base_url(Url::parse("http://localhost:3000/twirp/")?)?;
110-
let resp = client.make_hat(MakeHatRequest { inches: 1 }).await;
111-
eprintln!("{:?}", resp);
112-
}
113-
```
5+
- [`twirp-build`](https://github.com/github/twirp-rs/tree/main/crates/twirp/twirp-build) - A crate for generating twirp client and server interfaces. This is probably what you are looking for.
6+
- [`twirp`](https://github.com/github/twirp-rs/tree/main/crates/twirp/) - A crate used by code that is generated by `twirp-build`

crates/twirp-build/Cargo.toml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
[package]
22
name = "twirp-build"
33
version = "0.7.0"
4-
authors = ["The blackbird team <support@github.com>"]
54
edition = "2021"
65
description = "Code generation for async-compatible Twirp RPC interfaces."
76
readme = "README.md"
8-
keywords = ["twirp"]
9-
categories = ["network-programming"]
7+
keywords = ["twirp", "prost", "protocol-buffers"]
8+
categories = [
9+
"development-tools::build-utils",
10+
"network-programming",
11+
"asynchronous",
12+
]
1013
repository = "https://github.com/github/twirp-rs"
14+
license = "MIT"
15+
license-file = "./LICENSE"
1116

1217
[dependencies]
1318
prost-build = "0.13"

crates/twirp-build/LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2023 GitHub, Inc.
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

crates/twirp-build/README.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# `twirp-build`
2+
3+
[Twirp is an RPC protocol](https://twitchtv.github.io/twirp/docs/spec_v7.html) based on HTTP and Protocol Buffers (proto). The protocol uses HTTP URLs to specify the RPC endpoints, and sends/receives proto messages as HTTP request/response bodies. Services are defined in a [.proto file](https://developers.google.com/protocol-buffers/docs/proto3), allowing easy implementation of RPC services with auto-generated clients and servers in different languages.
4+
5+
The [canonical implementation](https://github.com/twitchtv/twirp) is in Go, this is a Rust implementation of the protocol. Rust protocol buffer support is provided by the [`prost`](https://github.com/tokio-rs/prost) ecosystem.
6+
7+
Unlike [`prost-twirp`](https://github.com/sourcefrog/prost-twirp), the generated traits for serving and accessing RPCs are implemented atop `async` functions. Because traits containing `async` functions [are not directly supported](https://smallcultfollowing.com/babysteps/blog/2019/10/26/async-fn-in-traits-are-hard/) in Rust versions prior to 1.75, this crate uses the [`async_trait`](https://github.com/dtolnay/async-trait) macro to encapsulate the scaffolding required to make them work.
8+
9+
## Usage
10+
11+
See the [example](./example) for a complete example project.
12+
13+
Define services and messages in a `.proto` file:
14+
15+
```proto
16+
// service.proto
17+
package service.haberdash.v1;
18+
19+
service HaberdasherAPI {
20+
rpc MakeHat(MakeHatRequest) returns (MakeHatResponse);
21+
}
22+
message MakeHatRequest { }
23+
message MakeHatResponse { }
24+
```
25+
26+
Add the `twirp-build` crate as a build dependency in your `Cargo.toml` (you'll need `prost-build` too):
27+
28+
```toml
29+
# Cargo.toml
30+
[build-dependencies]
31+
twirp-build = "0.3"
32+
prost-build = "0.13"
33+
```
34+
35+
Add a `build.rs` file to your project to compile the protos and generate Rust code:
36+
37+
```rust
38+
fn main() {
39+
let proto_source_files = ["./service.proto"];
40+
41+
// Tell Cargo to rerun this build script if any of the proto files change
42+
for entry in &proto_source_files {
43+
println!("cargo:rerun-if-changed={}", entry);
44+
}
45+
46+
prost_build::Config::new()
47+
.type_attribute(".", "#[derive(serde::Serialize, serde::Deserialize)]") // enable support for JSON encoding
48+
.service_generator(twirp_build::service_generator())
49+
.compile_protos(&proto_source_files, &["./"])
50+
.expect("error compiling protos");
51+
}
52+
```
53+
54+
This generates code that you can find in `target/build/your-project-*/out/example.service.rs`. In order to use this code, you'll need to implement the trait for the proto defined service and wire up the service handlers to a hyper web server. See [the example `main.rs`]( example/src/main.rs) for details.
55+
56+
Include the generated code, create a router, register your service, and then serve those routes in the hyper server:
57+
58+
```rust
59+
mod haberdash {
60+
include!(concat!(env!("OUT_DIR"), "/service.haberdash.v1.rs"));
61+
}
62+
63+
use axum::Router;
64+
use haberdash::{MakeHatRequest, MakeHatResponse};
65+
66+
#[tokio::main]
67+
pub async fn main() {
68+
let api_impl = Arc::new(HaberdasherApiServer {});
69+
let twirp_routes = Router::new()
70+
.nest(haberdash::SERVICE_FQN, haberdash::router(api_impl));
71+
let app = Router::new()
72+
.nest("/twirp", twirp_routes)
73+
.fallback(twirp::server::not_found_handler);
74+
75+
let tcp_listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
76+
if let Err(e) = axum::serve(tcp_listener, app).await {
77+
eprintln!("server error: {}", e);
78+
}
79+
}
80+
81+
// Define the server and implement the trait.
82+
struct HaberdasherApiServer;
83+
84+
#[async_trait]
85+
impl haberdash::HaberdasherApi for HaberdasherApiServer {
86+
async fn make_hat(&self, ctx: twirp::Context, req: MakeHatRequest) -> Result<MakeHatResponse, TwirpErrorResponse> {
87+
todo!()
88+
}
89+
}
90+
```
91+
92+
This code creates an `axum::Router`, then hands it off to `axum::serve()` to handle networking.
93+
This use of `axum::serve` is optional. After building `app`, you can instead invoke it from any
94+
`hyper`-based server by importing `twirp::tower::Service` and doing `app.call(request).await`.
95+
96+
## Usage (client side)
97+
98+
On the client side, you also get a generated twirp client (based on the rpc endpoints in your proto). Include the generated code, create a client, and start making rpc calls:
99+
100+
``` rust
101+
mod haberdash {
102+
include!(concat!(env!("OUT_DIR"), "/service.haberdash.v1.rs"));
103+
}
104+
105+
use haberdash::{HaberdasherApiClient, MakeHatRequest, MakeHatResponse};
106+
107+
#[tokio::main]
108+
pub async fn main() {
109+
let client = Client::from_base_url(Url::parse("http://localhost:3000/twirp/")?)?;
110+
let resp = client.make_hat(MakeHatRequest { inches: 1 }).await;
111+
eprintln!("{:?}", resp);
112+
}
113+
```

0 commit comments

Comments
 (0)