Compare commits
2 Commits
821dcdf277
...
8e72bedcf1
Author | SHA1 | Date | |
---|---|---|---|
8e72bedcf1 | |||
59d4123321 |
75
README.md
Normal file
75
README.md
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# `bebot`
|
||||||
|
|
||||||
|
Bebot is a Gitlab webhook handler that publishes messages to Matrix when
|
||||||
|
interesting things happen in your configured repos.
|
||||||
|
|
||||||
|
Currently-supported Gitlab event types:
|
||||||
|
|
||||||
|
* Push events
|
||||||
|
* Tag push events
|
||||||
|
* Issue events
|
||||||
|
* Merge request events
|
||||||
|
* Pipeline events (only publishes on failure for now)
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
Bebot is written in Rust, and requires a Rust toolchain in order to
|
||||||
|
build. The usual `cargo build` or `cargo build --release` will do the
|
||||||
|
trick.
|
||||||
|
|
||||||
|
You can also build and install the latest released version of Bebot by
|
||||||
|
running `cargo install bebot`.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
Bebot requires a configuration file in YAML format. See
|
||||||
|
`sample-config.yaml` for all existing configuration options. They
|
||||||
|
should hopefully be fairly self-explanatory, but a few clarifications:
|
||||||
|
|
||||||
|
1. `bind_address` and `bind_port` determine what IP/interface and port
|
||||||
|
the webhook handler will listen on.
|
||||||
|
2. Bebot's webhook will be served from `/hooks/gitlab`. You can use
|
||||||
|
`url_prefix` to prepend further path segments in front of that.
|
||||||
|
3. `user_id` and `password` are for the Matrix user that the bot will
|
||||||
|
sign into Matrix as. Ensure that `user_id` is the full username,
|
||||||
|
including the homeserver.
|
||||||
|
4. `default_room` is the room that Bebot will publish to if not
|
||||||
|
specified in the per-repo configuration.
|
||||||
|
5. `repo_configs` is a map where the key is of the form
|
||||||
|
`$GITLAB_INSTANCE/$NAMESPACE/$REPO`. It is recommended that you use
|
||||||
|
a unique, randomly-generated `token` for each repository. You can
|
||||||
|
specify `room` here as well if you don't want messages to go to
|
||||||
|
`default_room`.
|
||||||
|
|
||||||
|
When setting up the webhook in Gitlab, use the same `token` from the
|
||||||
|
configuration file in the webhook's "Secret token" field. You should
|
||||||
|
only select "Push events", "Tag push events", "Issues events", "Merge
|
||||||
|
request events", and "Pipeline events". You can leave some of these out
|
||||||
|
if you don't want Bebot to publish messages for everything.
|
||||||
|
|
||||||
|
Bebot does not support serving the webhook over TLS, so you will
|
||||||
|
probably want to put it behind a reverse-proxy such as nginx.
|
||||||
|
|
||||||
|
In the `scripts` directory is a `set-webhook.py` script that can set up
|
||||||
|
(or update) webhooks for your repository, automatically generating a
|
||||||
|
token for you. If setting up the webhook for the first time, it will
|
||||||
|
output to stdout a YAML snippet that goes under the `repo_configs`
|
||||||
|
section of the configuration file. If you run the script with no
|
||||||
|
arguments, it will print out usage details.
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
After you've done all that, simply run Bebot:
|
||||||
|
|
||||||
|
```
|
||||||
|
bebot /path/to/config-file.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
You can set the `BEBOT_LOG` environment variable to increase or decrease
|
||||||
|
logging verbosity. (Try `debug`, `info`, `warn` `error`, or `off`.)
|
||||||
|
|
||||||
|
### Docker
|
||||||
|
|
||||||
|
A `Dockerfile` is also provided. When running the container it builds,
|
||||||
|
mount the configuration file so it appears inside the container as
|
||||||
|
`/bebot/config/bebot.yaml`.
|
12
sample-config.yaml
Normal file
12
sample-config.yaml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
bind_address: 127.0.0.1
|
||||||
|
bind_port: 3000
|
||||||
|
url_prefix: "/bebot"
|
||||||
|
user_id: "@mybebot:example.com"
|
||||||
|
password: "secret-matrix-account-password"
|
||||||
|
default_room: "#my-project-commits:example.com"
|
||||||
|
repo_configs:
|
||||||
|
"gitlab.example.com/myorg/my-cool-app":
|
||||||
|
token: "abcdefg12345"
|
||||||
|
"gitlab.example.com/myuser/some-other-less-cool-app":
|
||||||
|
room: "#my-other-room:example.com"
|
||||||
|
token: "kljaslkdjaklsdjalksd"
|
@ -202,6 +202,8 @@ pub enum GitlabEvent {
|
|||||||
user: User,
|
user: User,
|
||||||
project: Project,
|
project: Project,
|
||||||
},
|
},
|
||||||
|
#[serde(other)]
|
||||||
|
Other,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GitlabEventExt for GitlabEvent {
|
impl GitlabEventExt for GitlabEvent {
|
||||||
@ -212,6 +214,7 @@ impl GitlabEventExt for GitlabEvent {
|
|||||||
GitlabEvent::Issue { project, .. } => project,
|
GitlabEvent::Issue { project, .. } => project,
|
||||||
GitlabEvent::MergeRequest { project, .. } => &project,
|
GitlabEvent::MergeRequest { project, .. } => &project,
|
||||||
GitlabEvent::Pipeline { project, .. } => &project,
|
GitlabEvent::Pipeline { project, .. } => &project,
|
||||||
|
GitlabEvent::Other => unreachable!("Unsupported event type"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -222,6 +225,7 @@ impl GitlabEventExt for GitlabEvent {
|
|||||||
GitlabEvent::Issue { .. } => None,
|
GitlabEvent::Issue { .. } => None,
|
||||||
GitlabEvent::MergeRequest { object_attributes, .. } => Some(&object_attributes.target_branch),
|
GitlabEvent::MergeRequest { object_attributes, .. } => Some(&object_attributes.target_branch),
|
||||||
GitlabEvent::Pipeline { object_attributes, .. } => Some(&object_attributes.r#ref),
|
GitlabEvent::Pipeline { object_attributes, .. } => Some(&object_attributes.r#ref),
|
||||||
|
GitlabEvent::Other => unreachable!("Unsupported event type"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,6 +236,7 @@ impl GitlabEventExt for GitlabEvent {
|
|||||||
GitlabEvent::Issue { user, .. } => &user.name,
|
GitlabEvent::Issue { user, .. } => &user.name,
|
||||||
GitlabEvent::MergeRequest { user, .. } => &user.name,
|
GitlabEvent::MergeRequest { user, .. } => &user.name,
|
||||||
GitlabEvent::Pipeline { user, .. } => &user.name,
|
GitlabEvent::Pipeline { user, .. } => &user.name,
|
||||||
|
GitlabEvent::Other => unreachable!("Unsupported event type"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -329,6 +334,7 @@ impl GitlabEventExt for GitlabEvent {
|
|||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
GitlabEvent::Other => unreachable!("Unsupported event type"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
44
src/main.rs
44
src/main.rs
@ -185,27 +185,37 @@ async fn run() -> anyhow::Result<()> {
|
|||||||
let event_tx = event_tx.clone();
|
let event_tx = event_tx.clone();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let project = event.project();
|
match event {
|
||||||
let config_key = project.web_url.replace("http://", "").replace("https://", "");
|
GitlabEvent::Other => {
|
||||||
if let Some(repo_config) = config.repo_configs.get(&config_key) {
|
warp::reply::with_status("Unsupported Gitlab event type", StatusCode::BAD_REQUEST)
|
||||||
if !constant_time_eq(token.as_bytes(), repo_config.token.as_bytes()) {
|
}
|
||||||
warn!("Invalid token for repo '{}'", config_key);
|
_ => {
|
||||||
warp::reply::with_status("Invalid token", StatusCode::FORBIDDEN)
|
let project = event.project();
|
||||||
} else {
|
let config_key = project.web_url.replace("http://", "").replace("https://", "");
|
||||||
debug!("payload: {:?}", event);
|
if let Some(repo_config) = config.repo_configs.get(&config_key) {
|
||||||
if let Some(room) = repo_config.room.as_ref().or(config.default_room.as_ref()) {
|
if !constant_time_eq(token.as_bytes(), repo_config.token.as_bytes()) {
|
||||||
if let Err(err) = event_tx.send((event, room.clone())).await {
|
warn!("Invalid token for repo '{}'", config_key);
|
||||||
warn!("Failed to enqueue payload: {}", err);
|
warp::reply::with_status("Invalid token", StatusCode::FORBIDDEN)
|
||||||
|
} else {
|
||||||
|
debug!("payload: {:?}", event);
|
||||||
|
if let Some(room) = repo_config.room.as_ref().or(config.default_room.as_ref()) {
|
||||||
|
if let Err(err) = event_tx.send((event, room.clone())).await {
|
||||||
|
warn!("Failed to enqueue payload: {}", err);
|
||||||
|
}
|
||||||
|
warp::reply::with_status("OK", StatusCode::OK)
|
||||||
|
} else {
|
||||||
|
info!("Channel not configured for repo '{}'", config_key);
|
||||||
|
warp::reply::with_status(
|
||||||
|
"Matrix room not configured for repo",
|
||||||
|
StatusCode::NOT_FOUND,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
warp::reply::with_status("OK", StatusCode::OK)
|
|
||||||
} else {
|
} else {
|
||||||
info!("Channel not configured for repo '{}'", config_key);
|
info!("Repo '{}' unconfigured", config_key);
|
||||||
warp::reply::with_status("Matrix room not configured for repo", StatusCode::NOT_FOUND)
|
warp::reply::with_status("Repo not configured", StatusCode::NOT_FOUND)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
info!("Repo '{}' unconfigured", config_key);
|
|
||||||
warp::reply::with_status("Repo not configured", StatusCode::NOT_FOUND)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user