Skip to content

Commit b0c005d

Browse files
committed
Initial commit
0 parents  commit b0c005d

File tree

12 files changed

+404
-0
lines changed

12 files changed

+404
-0
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
name: Build and Publish Docker Image
2+
3+
on:
4+
push:
5+
branches:
6+
- master
7+
tags:
8+
- 'v*'
9+
pull_request:
10+
branches:
11+
- master
12+
workflow_dispatch:
13+
14+
env:
15+
REGISTRY: ghcr.io
16+
IMAGE_NAME: ${{ github.repository }}
17+
18+
jobs:
19+
build-and-push:
20+
runs-on: ubuntu-latest
21+
permissions:
22+
contents: read
23+
packages: write
24+
25+
steps:
26+
- name: Checkout repository
27+
uses: actions/checkout@v4
28+
29+
- name: Log in to the Container registry
30+
if: github.event_name != 'pull_request'
31+
uses: docker/login-action@v3
32+
with:
33+
registry: ${{ env.REGISTRY }}
34+
username: ${{ github.actor }}
35+
password: ${{ secrets.GITHUB_TOKEN }}
36+
37+
- name: Extract metadata (tags, labels) for Docker
38+
id: meta
39+
uses: docker/metadata-action@v5
40+
with:
41+
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
42+
tags: |
43+
type=ref,event=branch
44+
type=ref,event=pr
45+
type=semver,pattern={{version}}
46+
type=semver,pattern={{major}}.{{minor}}
47+
type=sha
48+
49+
- name: Build and push Docker image
50+
uses: docker/build-push-action@v5
51+
with:
52+
context: .
53+
file: src/Dockerfile
54+
push: ${{ github.event_name != 'pull_request' }}
55+
tags: ${{ steps.meta.outputs.tags }}
56+
labels: ${{ steps.meta.outputs.labels }}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
out
2+
.idea

LICENSE.md

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) 2026 Adomi
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.

README.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
> [!TIP]
2+
> Looking for a way to manage your API contracts effortlessly?
3+
> Check out our community-driven tools for Odoo and beyond.
4+
>
5+
> **[adomi-io](https://github.com/adomi-io)**.
6+
7+
<p align="center">
8+
<img src="docs/static/logo.png" width="240" />
9+
</p>
10+
11+
# 📜 Contract Builder - Protobuf 🏗️
12+
13+
A tool to turn your Protocol Buffer definitions into working code. Drop your `.proto` files in, and the builder automatically generates clients and types for your favorite languages.
14+
15+
Under the hood, this repo uses `protoc` wrapped in Docker, including support for Betterproto, Go, and TypeScript, making it easy to keep your services in sync without manual boilerplate.
16+
17+
## Highlights
18+
19+
- 🌍 Support for **multiple** targets (Python, Betterproto, Go, TypeScript)
20+
- 📂 **Multi-service support**: Automatically scans the `specs` folder for all your services
21+
- 🐳 **Fully Dockerized**: No need to install `protoc` or plugins locally
22+
- 🔄 **Consistent Output**: Ensures your whole team generates the exact same code
23+
- 🚀 **Fast Iteration**: Quickly design your data models and see the code update instantly
24+
25+
## What you can do
26+
27+
- Keep a single source of truth for your service contracts
28+
- Generate multiple clients (including Betterproto) in one go
29+
- Ensure your frontend types always match your backend Protobuf definitions
30+
- Quickly iterate on service designs and see the code update instantly
31+
32+
To generate clients, just drop your Protobuf files into the `specs` folder and run the builder.
33+
34+
# Getting started
35+
36+
> [!WARNING]
37+
> This tool is designed to run via Docker.
38+
> It keeps your environment clean and ensures everyone on your team gets the exact same code output.
39+
>
40+
>**[Download Docker Desktop](https://www.docker.com/products/docker-desktop/)**
41+
42+
# Docker Compose
43+
44+
The easiest way to use this is with `docker-compose`. It mounts your local folders so the generated code appears right on your machine.
45+
46+
See the [docker](./docker) folder for more information.
47+
48+
Copy the files in the [docker](./docker) folder to your project root, or run the commands from within that folder.
49+
50+
**Place your specs**
51+
52+
Put your `.proto` files in subfolders under `specs/` (e.g., `specs/petshop/pet.proto`). The subfolder name determines the output name.
53+
54+
**Run the generator**
55+
56+
```bash
57+
docker compose up
58+
```
59+
60+
**Find your code**
61+
62+
Check the `out/` directory for your generated clients.
63+
64+
# Adding new targets
65+
66+
The logic lives in `src/generate.sh`, which uses the `GENERATORS` environment variable to determine which clients to build.
67+
68+
When using the provided `docker-compose.yml`, it defaults to `python,betterproto,go,typescript`.
69+
70+
## Update docker-compose.yml
71+
72+
Edit the `environment` section in your `docker-compose.yml`:
73+
74+
```yaml
75+
environment:
76+
- GENERATORS=python,betterproto,go,typescript
77+
```
78+
79+
## Run with an environment variable
80+
81+
You can also pass it directly to Docker:
82+
83+
```bash
84+
docker run --rm -e GENERATORS="python,go" -v $(pwd)/specs:/local/src -v $(pwd)/out:/local/out contract-builder-protobuf
85+
```
86+
87+
### Additional Options
88+
89+
You can pass additional arguments to `protoc` for specific generators using environment variables. This follows the naming convention `GENERATOR_{LANGUAGE}_ARGS`.
90+
91+
By default, the script calls `protoc` with `--{language}_out=...`. Use the `GENERATORS` list to specify the plugin name (e.g., `python_betterproto`, `ts_proto`, or just `go`).
92+
93+
For example, to pass options to the `go` generator or use `betterproto`:
94+
95+
```yaml
96+
environment:
97+
- GENERATORS=go,python_betterproto
98+
- GENERATOR_GO_ARGS=--go-grpc_out=/local/out/petshop/go --go_opt=paths=source_relative
99+
- GENERATOR_PYTHON_BETTERPROTO_ARGS=--python_betterproto_opt=unwrapped
100+
```
101+
102+
The language name is converted to uppercase, and hyphens are replaced with underscores.
103+
104+
## Typical data flow
105+
106+
- Developer updates `specs/petshop/pet.proto`
107+
- `docker compose up` is triggered
108+
- The generator container starts, scans `specs/`, and runs `generate.sh`
109+
- New code is written to `out/petshop/python`, `out/petshop/betterproto`, `out/petshop/go`, etc., depending on your `GENERATORS` setting.
110+
- Your app uses the updated clients immediately
111+
112+
# Running from source
113+
114+
Clone this repository, and open a terminal in the root directory.
115+
116+
## Build the application
117+
118+
> [!TIP]
119+
> You can also run `docker compose up --build`.
120+
121+
Run `docker compose build`
122+
123+
## Run the application
124+
125+
Run the application by running
126+
127+
`docker compose up`
128+
129+
If you want to run the application all the time, start it with
130+
131+
`docker compose up -d`
132+
133+
To stop the application, run
134+
135+
`docker compose down`
136+
137+
## About Adomi
138+
139+
Contract Builder is an Adomi project. We build helpful tools for modern development workflows. If you have ideas or run into issues, feel free to open an issue or suggestion.

docker-compose.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
services:
2+
generator:
3+
build:
4+
dockerfile: ./src/Dockerfile
5+
context: .
6+
environment:
7+
- GENERATORS=python,python_betterproto,go,ts_proto
8+
- GENERATOR_GO_ARGS=--go-grpc_out=/local/out/petshop/go --go_opt=paths=source_relative --go-grpc_opt=paths=source_relative
9+
- GENERATOR_TS_PROTO_ARGS=--ts_proto_opt=esModuleInterop=true
10+
volumes:
11+
- ./specs:/local/src:ro
12+
- ./out:/local/out

docker/docker-compose.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
services:
2+
generator:
3+
image: ghcr.io/adomi-io/contract-builder-protobuf:latest
4+
environment:
5+
- GENERATORS=python,betterproto,go,typescript
6+
volumes:
7+
- ./specs:/local/src:ro
8+
- ./out:/local/out

docker/specs/.gitkeep

Whitespace-only changes.

docs/static/logo.png

12 KB
Loading

specs/petshop/pet.proto

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
syntax = "proto3";
2+
3+
package petshop.v1;
4+
5+
option go_package = "github.com/adomi-io/contract-builder-protobuf/petshop/v1";
6+
7+
message Pet {
8+
int64 id = 1;
9+
string name = 2;
10+
PetType type = 3;
11+
repeated string tags = 4;
12+
}
13+
14+
enum PetType {
15+
PET_TYPE_UNSPECIFIED = 0;
16+
PET_TYPE_CAT = 1;
17+
PET_TYPE_DOG = 2;
18+
PET_TYPE_BIRD = 3;
19+
}

specs/petshop/shop.proto

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
syntax = "proto3";
2+
3+
package petshop.v1;
4+
5+
import "petshop/pet.proto";
6+
7+
option go_package = "github.com/adomi-io/contract-builder-protobuf/petshop/v1";
8+
9+
service PetShopService {
10+
rpc GetPet(GetPetRequest) returns (GetPetResponse);
11+
rpc ListPets(ListPetsRequest) returns (ListPetsResponse);
12+
}
13+
14+
message GetPetRequest {
15+
int64 id = 1;
16+
}
17+
18+
message GetPetResponse {
19+
Pet pet = 1;
20+
}
21+
22+
message ListPetsRequest {
23+
int32 page_size = 1;
24+
string page_token = 2;
25+
}
26+
27+
message ListPetsResponse {
28+
repeated Pet pets = 1;
29+
string next_page_token = 2;
30+
}

0 commit comments

Comments
 (0)