An automatic version checker bot.
Tired of manually updating dependency for your Docker containers, Helm charts, and Landscaper configurations?
The PR bot consumes modules from various types of artifact repositories (helm, docker, and git), determines dependencies between components in each, and automatically carries version changes down the dependency tree. It files pull requests to the relevant GitHub repositories on your behalf and notifies you if and when your CI workflow validates the changes.
The pr-bot reads module information for Docker and Helm artifacts, both in source and binary (packaged) forms. Automatic updates are supported according to this table:
Destination | ||||
---|---|---|---|---|
Type | Docker | Helm | Landscaper | |
Source | Docker | soon | yes, values.yaml |
n/a |
Helm | n/a | yes, requirements.yaml |
yes | |
Landscaper | n/a | n/a | n/a |
(support for additional module types coming soon, including Landscaper)
The pr-bot aims to support development workflows resembling the following:
- Source for a Docker container
foo
is updated and pushed to GitHub - CI/CD process pushes new container to Docker Hub, updates commit status in GitHub
- Helm chart
bar
depends on containerfoo
, and should be updated to the latest version. A pull request that updates the required version is filed. - A new version of Helm chart
bar
is released - Helm chart
baz
depends on the other chartbar
and should be updated to use the new version. A pull request is made to apply the update. - A Landscaper configuration depends on
baz
and should be updated to include the new version. A pull request is made to apply the update.
The pr-bot automates steps #3, #5, and #6. Ideally human interaction after step #1 should be limited to approving pull requests once CI/CD passes.
Copy config.yml.dist
to config.yml
and tweak as needed.
The PR bot's API uses tokens for authentication. To generate a token, the following can be used:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
The resulting string can be added to the tokens
list in config.yml
.
A new GitHub account should be created for machine use. The PR bot will use the account to host forks of your Git repositories and create pull requests to target repositories.
Create a new personal access token with the full repo
permission. Then,
add an entry to config.yml
:
github:
- domain: github.com
host: api.github.com
token: <NEW TOKEN HERE>
secret: <SHARED SECRET> # optional
The domain
should match the domain used in git remotes, while host
refers to
the GitHub API host itself. A pathPrefix
field can be optionally specified if
the GitHub API is not on the root domain, as may be the case for GitHub
Enterprise. If necessary, a proxy
field can also be set to direct requests to
that particular GitHub instance over some HTTP proxy.
If using the webhook handler, a secret
value can be generated using the above
instructions for generating a random token. This value should be provided in the
"Secret" field when creating the webhook in GitHub's UI. Note that secrets are
currently verified only when using Docker deployments; an alternative
authentication method is used when deploying to Google Cloud Functions.
HipChat rooms can optionally be added to deliver notifications when various
events occur. These are configured in the hipchat:
list in config.yml
.
Each entry should be a URL for a custom HipChat integration. These URLs
should end in /notification?access_token=xyz
. If no additional options are
needed, a plain string for the URL can be specified, but a block like the
following is also allowed:
url: https://my-hipchat-domain.com/v2/rooms/1234/notification?access_token=asdf
proxy: http://some-proxy:8080/
default: true
If default
is true
, additional messages may be delivered to the room
regarding various operational events for the PR bot itself. Otherwise,
notifications are only sent when a repository is added with the room
parameter
set (where a valid room ID would be 1234
from the above example).
There is no explicit limit to the number of HipChat URLs or blocks that can be
added, though only the first block with default: true
will be have operational
notifications delivered.
See the documentation for each deployment method:
- Google Cloud Functions - for public cloud deployments
- Designed to fit in free tier
- Docker - for public cloud or on-premise deployments
- Must be accessible to GitHub webhooks (public GitHub = public internet)
For Docker deployments, the (combined) endpoint is http://localhost:3000
. For
Google Cloud Functions deployments, the endpoint will vary depending on your
project and deployment options; the full addresses will be printed to the
console during deployment.
The API is used to manage the bot. It can be used to add and remove tracked repositories, manually trigger updates, and so on.
The API is REST-ish at best, as Google Cloud Functions doesn't expose
$PATH_INFO
or similar. It only listens on /
and dispatches requests based on
either the action
field in the posted JSON body or the GitHub event header.
All API requests must be authenticated, but this has different requirements depending on context:
- For GitHub events (where
X-GitHub-Event
is set) anX-Hub-Signature
header is requred (info) - For API calls a
token
field is required in the JSON request body and must match a token configured inconfig.yml
API examples use HTTPie, this is the recommended method for working with
the pr-bot API. The API is published at /bot
. Once the function is deployed to
either the emulator or a public GCF endpoint, the URL will be printed.
Due to limitations in GCF, all actions are handled via JSON blobs to in an HTTP POST. This plays well with HTTPie but is probably less than ideal for other clients (e.g. plain cURL).
Verify the API is working correctly by running:
http post http://localhost:8010/monasca-ci-testing/us-central1/bot \
token=deadbeef \
action=listRepositories
(be sure to change your endpoint and token as necessary).
If all is well, an empty JSON array should be returned. If deploying on public GCF, you may get an error about the datastore not being initialized for the current project. Follow the URL in the error message to resolve the issue (it will try to ask you to create an entity, but the window can be closed at this step - the datastore will already be initialized).
The pr-bot needs to track the source repository as well as the 'downstream' repository containing published artifacts.
First add the source repository:
http post http://localhost:8010/monasca-ci-testing/us-central1/bot \
token=deadbeef \
action=addRepository \
type=git \
name=my-helm-repo-git \
remote=https://github.com/my-org/my-helm-repo/
Then add the repository containing published helm charts:
http post http://localhost:8010/monasca-ci-testing/us-central1/bot \
token=deadbeef \
action=addRepository \
type=helm \
name=my-helm-repo
parent=my-helm-repo-git \
remote=https://myorg.github.io/my-helm-repo/
Note that repository remotes are (somewhat) lenient. For helm
-typed
repositories, /index.yaml
is optional and will be added automatically as
needed. Reverse lookups via remote should behave properly for all reasonable
forms of a remote.
(If HipChat rooms are configured, notifications can be enabled by setting
room=[room number]
in each of the above POST
s)
Note that Helm support has some limitations:
- The
type=helm
repository must haveparent=
set to the correctgit
repository - Updates are triggered on a
page_build
event from GitHub in the parent repository. In other words, this assumes your Helm repository is being published via GitHub Pages. Generic CI should be supported "soon", as long as GitHub status events are published. - Module names in the child must match those in the parent. That is, subdirectory names in your Helm charts' git repository must match chart names published in your Helm repository.
- Use of public / large repositories is not currently recommended. Support cor repository subsetting is necessary for this to work well. In other words, don't add any of the official Google Helm repositories!
The state of the pr-bot can be inspected using action=listRepositories
or
action=getRepository name=[repository name]
. All added repositories and
detected modules should be shown.
Note that the following all point to the REST endpoint. For
Lists all added repositories and their modules. Example:
Parameters: none
http post http://endpoint/ token=... action=listRepositories
List metadata and modules for a particular repository.
Parameters:
name
: the repository name
Like getRepository
, but fetches based on the remote
rather than the name
.
Remote lookups use fuzzy comparison
List detected dependent modules for some module. In other words, "if I update this module, what pull requests will be made?"
Parameters:
name
type
: the named repository type- one of:
git
,helm
,dockerhub
- one of:
remote
parent
: the name of the parent repository (optional)- note: required for most webhook handlers for binary push events
room
: the HipChat room number to notify for updates (optional)