Skip to content

Commit

Permalink
update documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
AdamNowotny committed Aug 2, 2024
1 parent b20e3a9 commit 6a21bb0
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 114 deletions.
86 changes: 41 additions & 45 deletions README.markdown
Original file line number Diff line number Diff line change
@@ -1,89 +1,85 @@
BuildReactor [![.github/workflows/build.yml](https://github.com/AdamNowotny/BuildReactor/actions/workflows/build.yml/badge.svg)](https://github.com/AdamNowotny/BuildReactor/actions/workflows/build.yml)
============
# BuildReactor [![.github/workflows/build.yml](https://github.com/AdamNowotny/BuildReactor/actions/workflows/build.yml/badge.svg)](https://github.com/AdamNowotny/BuildReactor/actions/workflows/build.yml)

Developer notifications and dashboard for CI servers
* Gives you overview of all your builds in one place
* Supports multiple continuous integration servers
* Configurable notifications
* Fullscreen mode let's you setup information radiator for your team within minutes
* Works on Windows, Mac and Linux

Links
-----

- Gives you overview of all your builds in one place
- Supports multiple continuous integration servers
- Configurable notifications
- Fullscreen mode let's you setup information radiator for your team within minutes
- Works on Windows, Mac and Linux

## Links

[Chrome Web Store](https://chrome.google.com/webstore/detail/buildreactor/agfdekbncfakhgofmaacjfkpbhjhpjmp)

[Mozilla Add-ons](https://addons.mozilla.org/en-GB/firefox/addon/buildreactor-extension/)

Supported services
==================
# Supported services

Below are the supported CI servers.

* Servers using [XML cctray format](https://github.com/robertmaldon/cc_dashboard/blob/master/README.md#multiple-project-summary-reporting-standard)
* [Atlassian Bamboo](http://www.atlassian.com/software/bamboo/)
* [BuildBot](http://buildbot.net/)
* [BuildKite](https://buildkite.com/)
* [Jenkins](http://jenkins.io/)
* [TeamCity](http://www.jetbrains.com/teamcity/)
* [Travis-CI](http://travis-ci.org/)
- Servers using [XML cctray format](https://github.com/robertmaldon/cc_dashboard/blob/master/README.md#multiple-project-summary-reporting-standard)
- [Atlassian Bamboo](http://www.atlassian.com/software/bamboo/)
- [BuildBot](http://buildbot.net/)
- [BuildKite](https://buildkite.com/)
- [GitHub Actions](https://github.com/features/actions)
- [Jenkins](http://jenkins.io/)
- [TeamCity](http://www.jetbrains.com/teamcity/)
- [Travis-CI](http://travis-ci.org/)

Screenshots
===========
# Screenshots

## Notifications

Notifications
-------------
<img src="docs/notifications-640x400.png" alt="BuildReactor notifications">

Popup and chrome badge
----------------------
## Popup and chrome badge

<img src="docs/popup-640x400.png" alt="BuildReactor popup">

Options page - adding new service
---------------------------------
## Options page - adding new service

<img src="docs/settings-new-1280x800.png" alt="BuildReactor options page">

Options page - service settings
-------------------------------
## Options page - service settings

<img src="docs/settings-1280x800.png" alt="BuildReactor options page">

Options page - view configuration
---------------------------------
## Options page - view configuration

<img src="docs/settings-view-1280x800.png" alt="BuildReactor view configuration page">

Dashboard page
-------------------------------
## Dashboard page

<img src="docs/dashboard-1280x800.png" alt="BuildReactor dashboard">

Developer setup
===============
# Developer setup

It's recommneded to use VSCode with DevContainers extension (https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)

This will open project with all needed dependencies.

Installation (manual)
---------------------
## Installation (manual)

1. Install [Node.js](http://nodejs.org/)
2. Go to project directory and run:
- `npm install`
- `npm run dist`

- `npm install`
- `npm run dist`

3. Open Chrome Extension manager and `Load unpacked extension..` from `dist/BuildReactor` folder.

Testing
-------
## Testing

`npm test` - run unit tests

`npm run test:watch` - use during development to run tests continuously

Development
-----------
## Development

[Adding new services](docs/adding-new-services.markdown)

Legal
=====
# Legal

This code is distributed under Apache License version 2.0

Expand Down
150 changes: 81 additions & 69 deletions docs/adding-new-services.markdown
Original file line number Diff line number Diff line change
@@ -1,40 +1,42 @@
Adding new services
===================
# Adding new services

To add support for a new type of service in BuildReactor you need to implement three methods described below and register the service.

You can look up all the TypeScript types involved here:
https://github.com/AdamNowotny/BuildReactor/blob/master/src/services/service-types.ts#L71-L78

For examples look at implementations in the services folder:
- [Bamboo](../src/services/bamboo/bamboo.js)
- [BuildKite](../src/services/buildkite/buildkite.js)
- [Jenkins](../src/services/jenkins/jenkins.js)
- [Travis](../src/services/travis/travis.js)

Configuration
-------------
The `getInfo` method is called to get information about the new service type, for example available settings, icons, etc. At a minimum this needs to be implemented so the service can be registered and shown on the __New service__ page.
- [BuildKite](../src/services/buildkite/buildkite.ts)
- [GitHub Actions](../src/services/github/github.ts)
- [Jenkins](../src/services/jenkins/jenkins.ts)

## Configuration

The `getDefinition` method is called to get information about the new service type, for example available settings, icons, etc. At a minimum this needs to be implemented so the service can be registered and shown on the **New service** page.

Example:

```js
const getInfo = () => ({
const getDefinition = () => ({
typeName: 'My Service', // name shown when adding service
baseUrl: 'my-service', // service ID, same as the folder name file is in
icon: 'services/my-service/icon.png', // icon for browser notifications, /src/ is the base folder
logo: 'services/my-service/logo.svg', // Logo shown when adding new service
defaultConfig: {
baseUrl: 'my-service',
name: '',
projects: [],
token: '',
updateInterval: 60
pipelines: [],
token: ''
}
fields: [ // optional
fields: [
{
type: 'url', // available types: url, token, username, password
type: 'url', // available types: url, token, username, password, branch
name: 'Server URL', // label displayed above the field (optional)
config: 'url', // configuration property used to store the value (optional)
help: 'Help text for url' // additional field description (optional)
},
{ type: 'username' },
{ type: 'password' }
{ type: 'token' }
]
});
```
Expand All @@ -43,79 +45,89 @@ const getInfo = () => ({

Both `baseUrl` fields are required and should be the same as folder name in _/services/_ where you created your implementation.

`fields` specifies which UI elements will be available when configuring service instance. It is optional as by default the form uses the properties of the `defaultConfig` object to determine which fields should be shown. For details refer to [dynamicForm.html](../src/settings/service/directives/dynamicForm/dynamicForm.html)
`fields` specifies which UI elements will be available when configuring service instance. It is an optional list as by default the form uses the properties of the `defaultConfig` object to determine which fields should be shown. For details refer to [dynamicForm.html](../src/settings/service/directives/dynamicForm/dynamicForm.html)

List of available builds
------------------------
## List of available pipelines

Called when user presses *Show* on the configuration page. Should return a RxJS sequence with all available builds.
Called when user presses _Show_ on the configuration page. Should return a promise resolving to list of `CIPipeline` items.

Example (hard-coded values):

```js
const getAll = (settings) => Rx.Observable.fromArray([{
id: 'build_id', // unique ID, this will be passed to getLatest
name: 'Build name', // name shown in the UI
group: 'Group', // null or group the build belongs to if your CI server supports it
isDisabled: false // some servers report projects as not buildable/disabled
}]);
const getPipelines = async (settings: CIServiceSettings): Promise<CIPipeline[]> => {
return [
{
id: 'build_id', // unique ID, this will be passed to getLatestBuilds
name: 'Build name', // name shown in the UI
group: 'Group', // optional group the pipeline belongs to
isDisabled: false, // optional, some servers report projects as not buildable/disabled
},
];
};
```
The order of builds in the sequence is not important as they will be ordered according to `settings.projects`.

Latest status
-------------
## Latest status

`getLatest` function accepts service settings with array of build IDs to monitor.
`getLatestBuilds` function accepts service settings with array of pipeline IDs to monitor.

The example below uses [/src/core/services/request.js](../src/core/services/request.js) to make HTTP request for each build in `settings.projects` array and then parse the body of each response.
The example below uses [/src/service-worker/request.ts](../src/service-worker/request.ts) to make HTTP request for each build in `settings.pipelines` array and then parse the body of each response. `Request` is a wrapper around Fetch API that handles some common HTTP headers, parsing of XML and makes it easier to unit test against saved json responses.

Example:

```js
const getLatest = (settings) => Rx.Observable.fromArray(settings.projects)
.selectMany((id) => request
.get({ url: `https://server/builds/${id}.json` })
.select((response) => response.body)
.select((build) => ({
id: build.id, // required
name: build.name,
group: build.group,
webUrl: build.url, // clicking on the build will open the url
isBroken: build.state === 'failed', // shown as red if true
isRunning: build.active, // in progress, shown as animated stripes
isWaiting: build.queued, // scheduled, shown as stripes with no animation
isDisabled: false, // tag as disabled and display as grey
tags: [{ name: 'Canceled', type: 'warning' }], // other tags, type is optional (warning displayed yellow)
changes: (build.commits || []).map((commit) => ({
name: commit.username,
message: commit.message
})) // commit messages and committer name, likely to contain only one element
}))
.catch((ex) => Rx.Observable.return({
id,
error: ex
}))
const getLatestBuilds = async (settings: CIServiceSettings): Promise<CIBuild[]> => {
logger.debug('service.getLatestBuilds', settings);
const pipelines = settings.pipelines.map(async (id: string): Promise<CIBuild> => {
try {
const response = await request.get(
{ url: `https://server/builds/${id}.json` }
);
const [build] = response.body.builds; // get first build item
if (!build) throw new Error('Build not found');
return parseBuild(build);
} catch (ex: any) {
return {
id: id,
name: id,
error: { name: 'Error', message: ex.message },
};
}
},
);
return await Promise.all(pipelines);
};

const parseBuild = (build: any): CIBuild => {
return {
id: build.id, // required
name: build.name,
group: build.group,
webUrl: build.url, // clicking on the build will open the url
isBroken: build.state === 'failed', // shown as red if true
isRunning: build.active, // in progress, shown as animated stripes
isWaiting: build.queued, // scheduled, shown as stripes with no animation
isDisabled: false, // tag as disabled and display as grey
tags: [{ name: 'Canceled', type: 'warning' }], // other tags, type is optional (warning displayed yellow)
changes: (build.commits ?? []).map(commit => ({
name: commit.username,
message: commit.message,
})), // commit messages and committer name
}
};
```
The only required field is `id` however you want to implement as least `webUrl`, `isBroken` and `isRunning` to see the status of the build.
The only required fields are `id` and `name` however you want to implement as least `webUrl`, `isBroken` and `isRunning` to see the status of the build.
The _catch_ clause returns a build with `error` object. If the request or parsing fails then the `message` field from the error will be shown and the build will be tagged as *Offline*. The block is required so only the currently processed build is marked as error, otherwise if the whole sequence fails then all builds will be considered not accessible.
The _catch_ clause returns a build with `error` object. This is so only a single build is marked as _offline_ instead of the whole list in case the request or parsing throws an exception.
Existing implementations extract all available requests to a separate file to make unit testing easier. All protocol level concerns can be handled there (constructing the url, paging, authorisation, etc.).
## Registration
Registration
------------

After implementing the 3 functions you need to register the service to make it available. This is done in [/src/core/main.js](../src/core/main.js).
After implementing the 3 functions you need to register the service to make it available. This is done in [/src/services/service-repository.ts](../src/services/service-repository.ts).
Import the file:
```js
import myService from 'services/my-service/my-service';
```
then create a new instance of service using `poolingService`. This wraps your service to add error handling, call `getLatest` at regular intervals and push `serviceUpdated` event which is used by other parts of the extension to process the current build state.

The class created by `poolingService` has to be registered with `serviceController` to be available.

```js
serviceController.registerType(poolingService.create(myService));
```
then add your service implementing `CIService` to the `services` array in `init` method.

0 comments on commit 6a21bb0

Please sign in to comment.