Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
patriksvensson committed Jan 19, 2020
0 parents commit baf5e86
Show file tree
Hide file tree
Showing 54 changed files with 18,973 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
target
docs
ui/node_modules
16 changes: 16 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Pull request

on:
pull_request:
branches:
- master

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Build
run: cargo build
- name: Run tests
run: cargo test
29 changes: 29 additions & 0 deletions .github/workflows/push_release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Publish release

on:
release:
types: [published]

jobs:
build:
runs-on: ubuntu-latest
if: "!contains(github.event.head_commit.message, 'skip-ci')"
steps:
- uses: actions/checkout@v1

- name: Get the version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//}

- name: Build Docker image
run: |
docker build -t spectresystems/duck:latest -t spectresystems/duck:${{ steps.get_version.outputs.VERSION }} .
- name: Publish Docker image
env:
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_TOKEN }}
run: |
docker login -u ${DOCKER_USERNAME} -p "${DOCKER_PASSWORD}"
docker push spectresystems/duck:latest
docker push spectresystems/duck:${{ steps.get_version.outputs.VERSION }}
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Rust
/target
**/*.rs.bk

# Configuration
config.json
2,450 changes: 2,450 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

35 changes: 35 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "duck"
version = "0.1.0"
authors = ["Patrik Svensson <[email protected]>"]
edition = "2018"

[features]
docker = []

[[bin]]
name = "duck"
path = "src/main.rs"

[dependencies]
failure = "0.1.6"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
url = "2.1.0"
reqwest = "0.9.22"
waithandle = "0.1.0"
actix-web = "1.0.8"
actix-files = "0.1.7"
structopt = "0.3"
log = "0.4"
env_logger = "0.7.1"
actix-cors = "0.1.0"
chrono = "0.4.10"
regex = "1.3.3"

[profile.release]
panic = 'abort'
lto = true
codegen-units = 1
incremental = false
opt-level = "z"
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Build server
FROM ekidd/rust-musl-builder:stable AS server-builder
ADD . ./
RUN sudo chown -R rust:rust .
RUN cargo build --release --target x86_64-unknown-linux-musl --features docker

# Build UI
FROM node:lts-alpine as ui-builder
WORKDIR /app
ENV VUE_APP_MY_DUCK_SERVER=
COPY ./ui/package*.json ./
RUN npm install
COPY ./ui .
RUN npm run build

# Copy to Alpine container
FROM alpine:latest
EXPOSE 15825
RUN apk --no-cache add ca-certificates
COPY --from=server-builder /home/rust/src/target/x86_64-unknown-linux-musl/release/duck /usr/local/bin/
COPY --from=ui-builder /app/dist /usr/local/bin/ui
WORKDIR /usr/local/bin
ENTRYPOINT ["duck"]
CMD ["--help"]
134 changes: 134 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
# Duck

Duck is a build system agnostic build monitor written in Rust (backend server) and Vue (frontend). It also support sending messages to Slack and controlling [Philips Hue](https://www2.meethue.com/) lights.

![The frontend](res/images/frontend.png)

## Running

The absolute easiest way of getting started with Duck is to use the docker image. You will have to provide a configuration file for it to work.

```
> docker run --rm -it -v ./data:/data -p 8080:15825 spectresystems/duck:latest --config /data/duck.json
```

## How it works

The server part is responsible for collecting information from different `collectors` and forward interesting events to `observers`. The aggregated information is available for other services (such as the Vue frontend) via a HTTP API.

Observers can either be dependent on events from all collectors, or opt in to one or more collectors. This makes it easy to setup team specific build lights or Slack integration that's only dependent on specific collectors.

![Overview](res/images/overview.svg)

## Configuration

Below is an example configuration that specifies multiple collectors and observers. By specifying the schema you will get autocomplete support in editors that support it like Visual Studio Code.

```json
{
"$schema": "https://raw.githubusercontent.com/spectresystems/duck/master/config/schema.json",
"interval": 30,
"collectors": [
{
"teamcity": {
"id": "teamcity_local",
"serverUrl": "http://127.0.0.1:8111/",
"credentials": "guest",
"builds": [
"My_Project_Definition",
"My_Other_Build_Definition"
]
}
},
{
"teamcity": {
"id": "teamcity_other",
"enabled": false,
"serverUrl": "192.168.0.1:8111",
"credentials": {
"basic": {
"username": "foo",
"password": "bar"
}
},
"builds": [
"My_Third_Build_Definition"
]
}
},
{
"azure": {
"id": "azure_cake",
"enabled": false,
"organization": "cake-build",
"project": "Cake",
"credentials": "anonymous",
"definitions": [ "1", "3", "5" ],
"branches": [
"refs/heads/develop",
"refs/heads/main"
]
}
},
{
"azure": {
"id": "azure_private",
"enabled": false,
"organization": "some-organization",
"project": "some-project",
"credentials": {
"pat": "SUPER-SECRET-PERSONAL-ACCESS-TOKEN"
},
"definitions": [ "1" ],
"branches": [
"refs/heads/master"
]
}
}
],
"observers": [
{
"hue": {
"id": "hue_team_1_builds",
"collectors": [
"teamcity_local"
],
"hubUrl": "http://192.168.1.99",
"username": "THE-HUE-USERNAME",
"lights": [ "1" ]
}
},
{
"hue": {
"id": "hue_all_builds_dimmed",
"hubUrl": "http://192.168.1.99",
"username": "THE-HUE-USERNAME",
"brightness": 50,
"lights": [ "2", "3" ]
}
},
{
"slack": {
"id": "slack_team1",
"collectors": [ "teamcity_local" ],
"credentials": {
"webhook": {
"url": "https://hooks.slack.com/services/MY-WEBHOOK-URL"
}
}
}
},
{
"mattermost": {
"id": "mattermost",
"channel": "build-status",
"credentials": {
"webhook": {
"url": "https://mattermost.example.com"
}
}
}
}
]
}
```
106 changes: 106 additions & 0 deletions config/example.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
{
"$schema": "./schema.json",
"interval": 30,
"collectors": [
{
"teamcity": {
"id": "teamcity_local",
"serverUrl": "http://127.0.0.1:8111/",
"credentials": "guest",
"builds": [
"My_Project_Definition",
"My_Other_Build_Definition"
]
}
},
{
"teamcity": {
"id": "teamcity_other",
"enabled": false,
"serverUrl": "192.168.0.1:8111",
"credentials": {
"basic": {
"username": "foo",
"password": "bar"
}
},
"builds": [
"My_Third_Build_Definition"
]
}
},
{
"azure": {
"id": "azure_cake",
"enabled": false,
"organization": "cake-build",
"project": "Cake",
"credentials": "anonymous",
"definitions": [ "1", "3", "5" ],
"branches": [
"refs/heads/develop",
"refs/heads/main"
]
}
},
{
"azure": {
"id": "azure_private",
"enabled": false,
"organization": "some-organization",
"project": "some-project",
"credentials": {
"pat": "SUPER-SECRET-PERSONAL-ACCESS-TOKEN"
},
"definitions": [ "1" ],
"branches": [
"refs/heads/master"
]
}
}
],
"observers": [
{
"hue": {
"id": "hue_team_1_builds",
"collectors": [
"teamcity_local"
],
"hubUrl": "http://192.168.1.99",
"username": "THE-HUE-USERNAME",
"lights": [ "1" ]
}
},
{
"hue": {
"id": "hue_all_builds_dimmed",
"hubUrl": "http://192.168.1.99",
"username": "THE-HUE-USERNAME",
"brightness": 50,
"lights": [ "2", "3" ]
}
},
{
"slack": {
"id": "slack_team1",
"collectors": [ "teamcity_local" ],
"credentials": {
"webhook": {
"url": "https://hooks.slack.com/services/MY-WEBHOOK-URL"
}
}
}
},
{
"mattermost": {
"id": "mattermost",
"channel": "build-status",
"credentials": {
"webhook": {
"url": "https://mattermost.example.com"
}
}
}
}
]
}
Loading

0 comments on commit baf5e86

Please sign in to comment.