Buffy
Buffy is an editorial bots generator, a service to provide a bot helping scientific journals manage submission reviews.
Buffy is a configurable Ruby application that –once deployed as a web service listening to incoming GitHub webhooks– provides a bot that interacts during the peer-review process with editors, reviewers and authors to help them perform actions on the review, the software being reviewed and its corresponding paper, automating common editorial tasks like those needed by the Journal of Open Source Software, rOpenSci or Scipy.
Buffy is an Open Source project, the code is hosted at GitHub and released under a MIT license.
Installation
Buffy works listening to events received from GitHub and deciding if/how to reply by passing the received payload to different Responders. You can fork Buffy and configure the responders you want to use for any specific repository. There is no need for the Buffy fork to be located in the same GitHub user/organization as the repo where it will be used. To have Buffy ready and listening to events you can install it locally or deploy it to a server or service platform. You’ll need the following components:
A GitHub user to use as the bot with admin permissions on the target repo (usually a member of the organization owning the repo).
An instance of Buffy running
A webhook configured in the GitHub repo’s settings to send events to Buffy
Create the bot GitHub user
This will be the user responding to commands in the reviews repo.
1. Sign up at GitHub and create the bot user:
GitHub's signup page
2. Go to Settings >> Developer settings >> Personal access tokens
and create a new token with these scopes: public_repo
, repo:invite
, read:org
, read:user
and, if using the approval responder, admin:org
(as the app will create teams and invite members to the ropensci organization). Save that token, it will be your BUFFY_GH_ACCESS_TOKEN
.
Settings >> Developer settings >> Personal access tokens
3. Give the bot admin permissions: add it as member of the organization owning the repo where the reviews will take place:
People at GitHub Organization
Deploy Buffy
Server requirements
Some applications and services must be available to use by Buffy:
Redis: To process background jobs Buffy needs
redis
installed.Gitinspector: The Respository Checks Responder performs a statistical analysis using it.
cloc: The Respository Checks Responder can analyze source code, to run this check
cloc
is used.
Deployment
We will use here Heroku as an example service to deploy Buffy but you can use any other server or platform.
1. Create a new app in heroku linked to the url of your fork of Buffy. Automatically Heroku will use the heroku/ruby
buildpack.
To process background jobs Buffy needs
redis
installed, several add-ons providing it are available: Heroku Redis, RedisGreen, Redis To Go, etc.To install the
cloc
dependency there’s a buildpack for Heroku available here.Gitinspector can be installed using npm. To do so in Heroku, the
heroku/nodejs
buildpack can be added.
2. In the app settings add the following Config Vars:
BUFFY_BOT_GH_USER: <the_github_username_of_the_bot>
BUFFY_GH_ACCESS_TOKEN: <the_access_token_for_the_bot_created_in_the_previous_step>
BUFFY_GH_SECRET_TOKEN: <a_random_string>
RACK_ENV: production
2b. You can set the Ruby version to install using the CUSTOM_RUBY_VERSION env var. Unless you need any other specific version, please add also a Config Var named CUSTOM_RUBY_VERSION with the value of the latest version listed in the Buffy tested Ruby versions.
3. You can set Heroku to automatically redeploy when new commits are added. You can also add heroku as a git remote and deploy manually using
$ git push heroku main
There are detailed instructions in the Deploy section of your Heroku app.
4. You should now have a public URL with Buffy running. You can test that pointing your browser to /status
, like for example: https://your-new-buffy-deploy.herokuapp.com/status
It should return a simple up and running message.
Configure a webhook to send events from GitHub to Buffy
1. Go to the settings page of the repository where you want to use buffy. Add a new webhook.
Add webhook
2. Configure the new webhook with:
Payload URL: /dispatch path at your public buffy url
Content type: application/json
Secret: The BUFFY_GH_SECRET_TOKEN you configured in the previous step
Select individual events to trigger: issue comments and issues
New webhook
If everything went well you should have now your bot responding on the reviews issues. Try @botname help
for example.
Configuration
Buffy is configured using a simple YAML file containing all the settings needed. The settings file is located in the /config
dir and is named settings-<environment>.yml
, where <environment>
is the name of the environment Buffy is running in, usually set via the RACK_ENV env var. So for a Buffy instance running in production mode, the configuration file will be /config/settings-production.yml
A sample settings file will look similar to this:
buffy:
env:
bot_github_user: <%= ENV['BUFFY_BOT_GH_USER'] %>
gh_access_token: <%= ENV['BUFFY_GH_ACCESS_TOKEN'] %>
gh_secret_token: <%= ENV['BUFFY_GH_SECRET_TOKEN'] %>
teams:
editors: 3824115
eics: myorg/editor-in-chief-team
responders:
help:
hello:
hidden: true
assign_editor:
only: editors
remove_editor:
only: editors
no_editor_text: "TBD"
list_of_values:
- reviewers:
only: editors
if:
role_assigned: editor
reject_msg: "Can't assign reviewer because there is no editor assigned for this submission yet"
sample_value: "@username"
add_as_assignee: true
invite:
only: eics
set_value:
- version:
only: editors
sample_value: "v1.0.0"
- archive:
only: editors
sample_value: "10.21105/joss.12345"
welcome:
File Structure
The structure of the settings file starts with a single root node called buffy
.
It contains three main parts:
The
env
nodeThe
teams
nodeThe
responders
node
A detailed description of all of them:
Env: General configuration settings
env:
bot_github_user: <%= ENV['BUFFY_BOT_GH_USER'] %>
gh_access_token: <%= ENV['BUFFY_GH_ACCESS_TOKEN'] %>
gh_secret_token: <%= ENV['BUFFY_GH_SECRET_TOKEN'] %>
templates_path: ".templates"
The env section is used to declare general key/value settings. For security reasons is a good practice to load the secret values from your environment instead of hardcoding them in the code.
- bot_github_user
- The name of the bot. It is the GitHub user that will respond to commands. It should have admin permissions on the reviews repo. The default value is reading it from the BUFFY_BOT_GH_USER environment variable.
- gh_access_token
- The GitHub developer access token for the bot user. The default value is reading it from the BUFFY_GH_ACCESS_TOKEN environment variable.
- gh_secret_token
- The GitHub secret token configured for the webhook sending events to Buffy. The default value is reading it from the BUFFY_GH_SECRET_TOKEN environment variable.
- templates_path
- The relative path in the target repo where templates are located. This path is used by responders replying with a message built from a template. The default value is
.buffy/templates
.
Teams
teams:
editors: 3824117
eics: myorg/editor-in-chief-team
reviewers: 45363564
collaborators:
- user33
- user42
The optional teams node includes entries to reference GitHub teams, used later to grant access to responders only to users belonging to specific teams. The teams referred here must be visible teams of the organization owner of the repositories where the reviews will take place. Multiple entries can be added to the teams node. All entries follow this simple format:
- key: value
- Where key is the name for this team in Buffy and value can be:
- - The integer id of the team in GitHub (preferred)
- - The reference in format organization/name (for example: openjournals/editors)
- - An array of user handles
Responders
responders:
help:
hello:
hidden: true
assign_reviewers:
only: editors
The responders node lists all the responders that will be available. The key for each entry is the name of the responder and nested under it the configuration options for that responder are declared.
Common options
All the responders share some options available to all of them. They can also have their own particular configurable parameters (see docs for each responder). The common parameters are:
- hidden
- Defaults to false. If true this responder won't be listed in the help provided to users.
Usage:
... secret_responder: hidden: true ...
- only
- List of teams (referred by the name used in the teams node) that can have access to the responder. Used to limit access to the responder. If only and authorized_roles_in_issue are not present the responder is considered public and every user in the repository can invoke it.
Usage:
public_responder: available_for_one_team_responder: only: editors available_for_two_teams_responder: only: - editors - reviewers
- authorized_roles_in_issue
- List of values in the body of the issue marked with HTML comments that contains user(s) allowed to run the responder. Used to grant access to the responder per issue. If only and authorized_roles_in_issue are not present the responder is considered public and every user in the repository can invoke it.
Usage:
public_responder: restricted_responder: only: editors authorized_roles_in_issue: - author-handle - reviewers-list
(restricted_responder can only be called by members of the editors team and by users listed in the issue in the author-handle and reviewers-list HTML-marked fields)
- if
- This setting is used to impose conditions on the responder. It can include several options:
- title:
<String> or <Regular Expresion> Responder will run only if issue’ title matches this.
- body:
<String> or <Regular Expresion> Responder will run only if the body of the issue matches this.
- value_exists:
<String> Responder will run only if there is a not empty value for this in the issue (marked with HTML comments).
- value_matches:
<Hash> Responder will run only if the param values (marked with HTML comments) in the body of the issue matches the ones specified here.
- role_assigned:
<String> Responder will be run only if there is a username assigned for the specified value.
- labels:
<Array> Responder will be run only if the issue is labeled with all the labels listed here.
- reject_msg:
<String> Optional. The response to send as comment if the conditions are not met
Usage:
# This responder should be invoked only if there's an editor assigned # otherwise will reply with a custom "no editor assigned yet" message assign_reviewer: if: role_assigned: editor reject_msg: I can not do that because there is no editor assigned yet # This responder will run only if issue title includes '[PRE-REVIEW]' and if # there is a value for repo-url, ie: <!--repo-url-->whatever<!--end-repo-url--> start_review: if: title: "^\\[PRE-REVIEW\\]" value_exists: repo-url # This responder will run only if the value for submission_type in the body of # the issue matches 'astro', ie: <!--submission_type-->astro<!--end-submission_type--> start_review: if: value_matches: submission_type: astro # This responder will run only if issue title includes '[REVIEW]' and # the issue is labeled as 'accepted start_review: if: title: "^\\[REVIEW\\]" labels: - accepted
- description
- Every responder has a default description to be shown using the help_responder. Use this param if you want to use a custom description.
Usage:
... custom_responder: description: "This responder do something" ...
- example_invocation
- Every responder defines an example string showing the command to invoke it, to be listed using the help_responder. Use this param if for some reason you want to use a custom value for the example invocation.
Usage:
... custom_responder: example_invocation: "@botname run performance checks (please run this only on mondays)" ...
A complete example:
# Two responders are configured here:
#
# The assign_reviewers responder will respond only when triggered by a user that is
# member of any of the editors or editors-in-chief teams. It will also respond only
# in issues with the text "[REVIEW]" in its title and that have a not empty value
# in its body marked with HTML comments: <!--editor-->@EDITOR_HANDLE<!--end-editor-->
# Once invoked, it will label the issue with the 'reviewers-assigned' label.
#
# The hello responder is configured as hidden, so when calling the help responder the
# description and usage example of the hello responder won't be listed in the response.
...
responders:
assign_reviewers:
only:
- editors
- editors-in-chief
if:
title: "^\\[REVIEW\\]"
role_assigned: editor
add_labels:
- reviewers-assigned
description: "Use this command to assign a reviewers once the editor is assigned"
hello:
hidden: true
...
Several responders also allow adding or removing labels.
Multiple instances of the same responder
Sometimes you want to use a responder more than once, with different parameters. In that case under the name of the responder you can declare an array of instances, and the key for each instance will be passed to the responder as the name
parameter.
Example:
The set_value responder uses a name
param to change the value to a variable. If declared in the settings file like this:
responders:
set_value:
name: version
It could be invoked with @botname set 1.0 as version
.
If you want to use the same responder to change version
but also to allow editors to change url
you would declare multiple instances in the settings file like this:
responders:
set_value:
- version:
- url:
only: editors
Now @botname set 1.0 as version
is a public command and @botname set abcd.efg as url
is a command available to editors.
Available Responders
Buffy listens to events in the target repo using responders. Every responder is a subclass of the Responder
class.
Each responder have a define_listening
method where the action and/or regex the responder is listening to are defined.
The actions a responder takes if called are defined in the process_message
method.
Buffy includes a list of Responders that can be used by configuring them in the YAML settings file.
Help
The help responder provides a customizable command to list all the available actions. It only lists options available to the user triggering the responder and only responders not marked as hidden
.
Listens to
@botname help
help
is the default command, but it is customizable via params.
Settings key
help
Params
- help_command:
Optional. The command triggering this responder. Default value is help.
Examples
Simplest use case:
...
responders:
help:
...
Custom command:
...
responders:
help:
help_command: commands
...
Now it will reply to @botname commands
.
In action
Hello
A simple responder to reply to user greetings.
Listens to
Hi @botname
Hello @botname
Settings key
hello
Examples
Simplest use case:
...
responders:
hello:
...
Hidden from public commands list
...
responders:
hello:
hidden: true
...
In action
Basic command
This responder defines a custom command and replies with text messages, optionally using a template. Allows labeling.
Listens to
@botname <command>
For example, if you configure the command to be list editors, it would respond to:
@botname list editors
Settings key
basic_command
Params
- command:
The command this responder will listen to.
- description:
Optional String to show when the help command is invoked.
- example_invocation:
Optional String to show as an example of the command being used when the help command is invoked.
- message:
Optional A text message to use as reply.
- messages:
Optional <Array> A list of text messages to respond with.
- template_file:
Optional A template file to use to build the response message.
- data_from_issue:
<Array> An optional list of values that will be extracted from the issue’s body and used to fill the template.
- external_call:
Optional Configuration for a external service call. All available subparams are described in the external_service docs.
Examples
Simplest use case:
Reply with a preconfigured text
...
responders:
basic_command:
command: issue complaint
message: "Please send an email to reports@open.journal"
...
Multiple instances of the responder, multiple replies, using a template to respond:
...
responders:
basic_command:
- code_of_conduct:
command: code of conduct
description: Show our community Code of Conduct and Guidelines
messages:
- "Our CoC: https://github.com/openjournals/joss/blob/master/CODE_OF_CONDUCT.md"
- "It's adapted from the Contributor Covenant: http://contributor-covenant.org"
- "Reports of abusive or harassing behavior may be reported to reports@theoj.org"
- editor_list:
command: list editors
description: List all current topic editors
template_file: editors.md
...
In action
Multiple responses:
Replying with a template - The template file:
Replying with a template - In use:
Assign editor
Use this responder to update the value of the editor in the body of the issue. Allows labeling.
Listens to
@botname assign @username as editor
Requirements
The body of the issue should have the editor placeholder marked with HTML comments.
<!--editor--> <!--end-editor-->
Settings key
assign_editor
Params
- add_as_assignee:
<Boolean> If true, the editor user will be added as assignee to the issue. Default value is true.
- add_as_collaborator:
<Boolean> If true, the editor user will be added as collaborator to the repo. Default value is false.
- external_call:
Optional Configuration for a external service call. All available subparams are described in the external_service docs.
Examples
Simplest use case:
...
responders:
assign_editor:
...
Restricted to editors:
...
teams:
editors: 1111111
...
responders:
assign_editor:
only: editors
...
Restrict access to editors and add user as assignee and collaborator:
...
responders:
assign_editor:
only: editors
add_as_collaborator: true
...
In action
Initial state:
Invocation:
Final state:
Remove editor
This responder removes the assigned editor from the body of the issue (the one that can be assigned using the Assign Editor responder). The user will also be removed from the issue’s assignees. Allows labeling.
Listens to
@botname remove editor
Requirements
In the body of the issue the editor should be enclosed in HTML comments.
...
<!--editor--> @sarah_m_g <!--end-editor-->
...
Settings key
remove_editor
Params
- no_editor_text:
The text that will go in the editor place to state there’s no one assigned. The default value is Pending.
Examples
Simplest use case:
...
responders:
remove_editor:
...
Action restricted to editors:
...
teams:
editors: 1111111
...
responders:
remove_editor:
only: editors
...
Restrict access to editors, use custom text when there’s not editor:
...
responders:
remove_editor:
only: editors
no_editor_text: To be decided
...
In action
Initial state:
Invocation:
Final state:
Reviewers list
This responder adds/removes usernames to/from the list of reviewers in the body of the issue. Allows labeling.
Listens to
@botname add <username> as reviewer
@botname add <username> to reviewers
@botname remove <username> from reviewers
Requirements
The body of the issue should have the target field placeholder marked with HTML comments.
<!--reviewers-list--> <!--end-reviewers-list-->
Settings key
reviewers_list
Params
- sample_value:
<String> An optional sample value string for the target field. It is used for documentation purposes when the Help responder lists all available responders. Default value is @username.
- no_reviewers_text:
The text that will go in the reviewers list place to state there are no reviewers assigned yet. The default value is Pending.
- add_as_assignee:
<Boolean> Optional. If true, when adding a new reviewer will be added as assignee to the issue. Default value is false.
- add_as_collaborator:
<Boolean> Optional. If true, when adding a new reviewer will be added as collaborator to the repo. Default value is false.
Examples
Simplest case:
...
responders:
reviewers_list:
...
With different options:
...
responders:
reviewers_list:
only: editors
sample_value: "@reviewer-login"
add_as_assignee: true
...
In action
Initial state:
Adding a reviewer:
Reviewer added:
Removing a reviewer:
Reviewer removed:
Invite
This responder creates a repo invitation for a user to be added as collaborator so they have the needed permissions to edit comments. Use this responder to send an invitation to a user to collaborate in the review.
Listens to
@botname invite @username
Settings key
invite
Examples
Simplest use case:
...
responders:
invite:
...
Action restricted to users in the editors team:
...
teams:
editors: 1111111
...
responders:
invite:
only: editors
...
In action
Set value
This responder can be used to update the value of any field in the body of the issue. Allows labeling.
Listens to
@botname set <value> as <name>
For example, if you configure this responder to change the value of the version, it would respond to:
@botname set v1.0.3 as version
Requirements
The body of the issue should have the target field placeholder marked with HTML comments.
<!--<name>--> <!--end-<name>-->
Following the previous example if the name of the field is version:
<!--version--> <!--end-version-->
Settings key
set_value
Params
- name:
Required. The name of the target field in the body of the issue. It can be set using the
name:
keyword, or via the name of each instance if there are several instances of this responder specified in the settings file.- if_missing:
Optional Strategy when value placeholders are not defined in the body of the issue. Valid options: append (will add the value at the end of the issue body), prepend (will add the value at the beginning of the issue body) , error (will reply a not-found message). If this param is not present nothing will be done if value placeholder is not found.
- aliased_as:
Optional. The name of the value to be used in the command, in case it is different from the target field placeholder marked with HTML comments.
- heading:
if the value placeholder is missing and the if_missing strategy is set to append or prepend, when adding the value it will include this text as heading instead of just the value name.
- sample_value:
A sample value string for the target field. It is used for documentation purposes when the Help responder lists all available responders. Default value is xxxxx.
- template_file:
Optional A template file to use to build the response message (name and value are passed to it).
- external_call:
Optional Configuration for a external service call. All available subparams are described in the external_service docs.
Examples
Simplest use case:
...
responders:
set_value:
name: version
sample_value: v1.0.1
...
Multiple instances of the responder, some of them restricted to editors:
...
responders:
set_value:
- version:
only: editors
sample_value: "v1.0.0"
- archive:
only: editors
sample_value: "10.21105/joss.12345"
if_missing: prepend
heading: "Archive DOI"
- repository:
sample_value: "github.com/openjournals/buffy"
...
In action
Initial state:
Invocation:
Final state:
List of values
This responder adds values to/removes values from a list in the body of the issue. Allows labeling.
Listens to
@botname add <value> to <list-name>
@botname remove <value> from <list-name>
For example, if you configure this responder to add/remove values for the authors list, it would respond to:
@botname add @username to authors
Requirements
The body of the issue should have the target field placeholder marked with HTML comments.
<!--<listname>-list--> <!--end-<listname>-list-->
Following the previous example if the name of the field is authors:
<!--authors-list--> <!--end-authors-list-->
Settings key
list_of_values
Params
- name:
Required. The name of the list. It can be set using the
name:
keyword, or via the name of each instance if there are several instances of this responder specified in the settings file.- sample_value:
An optional sample value string for the target field. It is used for documentation purposes when the Help responder lists all available responders. Default value is xxxxx.
- add_as_assignee:
<Boolean> Optional. If true and the value is a user name, it will be added as assignee to the issue. Default value is false.
- add_as_collaborator:
<Boolean> Optional. If true and the value is a user name, it will be added as collaborator to the repo. Default value is false.
Examples
Simple case: A single list
...
responders:
list_of_values:
name: authors
...
Several lists with different options:
...
responders:
list_of_values:
- versions:
sample_value: "v1.0.2"
- authors
only: editors
sample_value: "@username"
add_as_collaborator: true
...
In action
Initial state:
Adding to the list:
Intermediate state:
Removing from the list:
Final state:
List team members
This responder replies with a list of members from a GitHub team
Listens to
@botname <command>
For example, if you configure the command to be list editors, it would respond to:
@botname list editors
Settings key
list_team_members
Params
- command:
The command this responder will listen to.
- team_id:
The id of the GitHub team to be listed.
- heading:
Optional Heading for the replied list.
- description:
Optional String to show when the help command is invoked.
Examples
List editors team members with custom heading
...
responders:
list_team_members:
command: list editors
team_id: 3824115
heading: Current journal editors
...
In action
Add/Remove assignee
This responder adds and removes users to the assignees list of the issue. Allows labeling.
Listens to
@botname add assignee: @username
@botname remove assignee: @username
Requirements
Only users that are collaborators in the target issue can be added as assignees. Otherwise the responder will reply with a not enough permissions message.
Settings key
add_remove_assignee
Examples
Simplest use case:
...
responders:
add_remove_assignee:
...
Hidden from commands list and restricted to editors:
...
responders:
add_remove_assignee:
only: editors
hidden: true
...
In action
Reviewer checklist comment
This responder adds a reviewer checklist editing the comment triggering the responder if the author of the comment is a reviewer. This way of adding checklists (instead of adding them to the body of the issue) does not require the reviewers to be collaborator of the repository, as they will be able to edit their own comments to update the progress of the checklist.
Listens to
@botname generate my checklist
Requirements
The checklist is read from a template file that should be available in the repository.
Settings key
reviewer_checklist_comment
Params
- template_file:
Required. The name of the template file to edit the comment with.
- data_from_issue:
<Array> An optional list of values that will be extracted from the issue’s body and used to fill the template.
- command:
Optional. The command triggering this responder. Default is generate my checklist
Examples
Simplest use case:
...
responders:
add_remove_checklist:
template_file: reviewer_checklist.md
...
Using info from the body to fill in the template. Custom command:
...
responders:
add_remove_checklist:
command: create reviewer checklist
template_file: reviewer_checklist.md
data_from_issue:
- target-repository
- author-handle
...
In action
The template:
Invocation:
Comment edited by the bot:
Add/Remove checklist
This responder adds and removes checklists for reviewers at the end of the body of the issue. Allows labeling.
Listens to
@botname add checklist for @username
@botname remove checklist for @username
Requirements
The checklist is read from a template file that should be available in the repository.
Settings key
add_remove_checklist
Params
- template_file:
Required. The name of the template file to append to the body.
- data_from_issue:
<Array> An optional list of values that will be extracted from the issue’s body and used to fill the template.
Examples
Simplest use case:
...
responders:
add_remove_checklist:
template_file: reviewer_checklist.md
...
Using info from the body to fill in the template. Action restricted to editors:
...
responders:
add_remove_checklist:
only: editors
template_file: reviewer_checklist.md
data_from_issue:
- target-repository
- author-handle
...
In action
The template:
Initial state:
Invocation:
Final state:
Label command
This responder defines a custom command to add and/or remove labels to the issue when invoked.
Listens to
@botname <command>
For example, if you configure the command to be review successful, it would respond to:
@botname review successful
Settings key
label_command
Params
- command:
The command this responder will listen to.
- add_labels:
<Array> A list of text labels to add to the issue.
- remove_labels:
<Array> A list of text labels to remove from the labels of the issue.
Examples
Simplest use case:
Just add a label.
...
responders:
label_command:
command: review successful
add_labels:
- recommend publication
...
Multiple instances of the responder, restricted to editors, adding and removing labels:
...
responders:
label_command:
- review_ok:
only: editors
command: review successful
add_labels:
- reviewed
- recommend publication
- pending publication
remove_labels:
- ongoing
- pending review
- review_nok:
only: editors
command: review failed
add_labels:
- recommend rejection
...
In action
Check references
This responder checks (asynchronously) the validity of the DOIs from a list of BibTeX entries (a paper’s references file).
Listens to
@botname check references
A non-default branch can be specified to look for the paper’s files in it:
@botname check references from branch <custom-branch-name>
Requirements
The target repository should include a paper.md or paper.tex file and its corresponding references file (paper.bib or paper.yml) with the BIbTeX entries.
The body of the issue should have the url of the repository with the paper’s files marked with HTML comments.
<!--target-repository--> URL HERE <!--end-target-repository-->
Settings key
check_references
Params
- url_field:
The optional name of the field marked with HTML comments where the URL of the repository with the paper is located. By default if this setting is not present, the value will be target-repository. Meaning Buffy will look for a string in the body of the issue between <!–target-repository–> and <!–end-target-repository–> HTML comments.
- branch_field:
The optional name of the field marked with HTML comments where the name of the branch is located. Defaults to branch (so Buffy will look for <!–branch–> and <!–end-branch–> in the body of the issue). If the setting is not present or the branch field is not found in the body of the issue, the default branch of the git repo will be used.
Examples
Simplest case:
...
check_references:
...
Buffy will clone the git repository specified between <!--target-repository-->
and <!--end-target-repository-->
marks and check the DOIs for all entries in the paper.bib file.
Example customizing fields:
...
check_references:
url_field: software-location
branch_field: branch-to-review
...
Buffy will clone the git repository specified between <!--software-location-->
and <!--end-software-location-->
marks, then checkout into the branch specified between <!--branch-to-review-->
and <!--end-branch-to-review-->
and then check the DOIs for all entries in the paper.bib file.
In action
Issue body with the repository's URL:
In use:
With non-default branch:
Repository checks
This responder performs (asynchronously) several checks on the target repository.
Listens to
@botname check repository
A non-default branch can be specified to run the checks on it:
@botname check repository from branch <custom-branch-name>
Requirements
The body of the issue should have the url of the repository marked with HTML comments.
<!--target-repository--> URL HERE <!--end-target-repository-->
Settings key
repo_checks
Params
- checks:
An optional list (Array) of checks to perform. If non present or empty all available checks will be run (see available checks for the values to use in the config file).
- url_field:
The optional name of the field marked with HTML comments where the URL of the repository with the paper is located. By default if this setting is not present, the value will be target-repository. Meaning Buffy will look for a string in the body of the issue between <!–target-repository–> and <!–end-target-repository–> HTML comments.
- branch_field:
The optional name of the field marked with HTML comments where the name of the branch is located. Defaults to branch (so Buffy will look for <!–branch–> and <!–end-branch–> in the body of the issue). If the setting is not present or the branch field is not found in the body of the issue, the default branch of the git repo will be used.
Available checks
The following values are valid for the :checks
list:
repo summary
: This check performs an analysis of the source code and list authorship, contributions and file types information.languages
: This will detect the languages used in the repository and tagged the issue with the top three used languages.wordcount
: This will count the number of words in the paper file.license
: This will look for an Open Source License in the target repo and reply an error message if no license is found.statement of need
: This check will look for an Statement of need section in the paper content.
Examples
Simplest case:
...
repo_checks:
...
Buffy will clone the git repository specified between <!--target-repository-->
and <!--end-target-repository-->
HTML comments and run all available checks.
Run selected checks:
...
repo_checks:
checks:
- repo summary
- languages
...
Buffy will only run the repo summary
and the languages
checks.
In action
Thanks
This responder replies when a user thanks the bot.
Listens to
Thanks @botname
Thank you @botname
@botname thanks
@botname thank you
Settings key
thanks
Params
- reply:
The message the bot will send back. Default value is “You are welcome”.
Examples
Simplest use case:
...
responders:
thanks:
...
Custom message and hidden from public commands list:
...
responders:
thanks:
reply: "No problem, I'm here to help!"
hidden: true
...
In action
Reminders
This responder allows to schedule a reminder for the author or a reviewer to return to a review after a certain period of time (supported units: days and weeks). The command will only work if the mentioned user is an author, a reviewer for the submission or the sender of the message (so editors can set reminders for themselves).
Listens to
@botname remind @username in 2 weeks
@botname remind @reviewer in 10 days
Settings key
reminders
Params
- reviewers:
Optional. The HTML-comment value name in the body of the issue to look for reviewers. Default value is reviewers-list.
- auhors:
Optional. The HTML-comment value name in the body of the issue to look for authors. Default value is author-handle.
Examples
Simple use case:
...
responders:
reminders:
only: editors
...
Custom html fields:
...
responders:
reminders:
only: editors
authors:
- author1
- author2
...
Now it will allow to set a reminder for all the users listed in the body of the issue in reviewers-list
, author1
and author2
HTML fields.
In action
Scheduling a reminder:
The reminder:
Initial values
This responder acts when a new issue is opened. It checks for the presence of placeholders in the body of the issue for all the configured values.
Listens to
New issue opened event.
Settings key
initial_values
Params
The values parameter is mandatory.
- values:
An array of values. Optionally each value can be individually customized.
For each value listed under values this options can be provided:
- heading:
Optional. If the value placeholder is missing when adding the value it will include this text as heading instead of just the value name. Default value is the value name capitalized bold.
- value:
Optional Value to add inside the HTML comments. Default value is empty string.
- action:
Optional Strategy when value placeholders are not defined in the body of the issue. Valid options: append (will add the value at the end of the issue body), prepend (will add the value at the beginning of the issue body). Default value is prepend
- warn_if_empty:
Optional If set to true if the placeholder for this value is not present or present but empty a new comment will be replied to the issue warning of the missing value. Default is false.
Examples
Simplest use case:
Verify presence of <!--version--><!--end-version-->
and <!--target-repository--><!--end-target-repository-->
in the body of the issue:
...
responders:
initial_values:
values:
- version
- target-repository
...
Multiple values with custom properties:
...
responders:
initial_values:
values:
- version:
- value: "vX.X.X"
- action: append
- author1:
- heading: "Author Github handle:"
- warn_if_empty: true
- target-repository
- archive
- package-name:
- warn_if_empty: true
...
In action
Using the Multiple values with custom properties example config:
Initial state:
Actual text in the initial body of the issue:
Final state:
Actual text in the final body of the issue:
Result:
version has been appended at the end of the body with a value of vX.X.X
author1 has been added with the custom heading
target-repository was already present, so nothing has been done with it
archive was already present, so nothing has been done with it
package-name was not present so it has been prepended.
New comment was created with a warning of missing values for package-name and author1
Welcome
This responder acts when a new issue is opened. It can reply with text messages, using a template, create a background job to asynchronously call an external service’s API and/or triggering another responder.
Allows labeling.
Listens to
New issue opened event.
Settings key
welcome
Requirements
When using a template to respond:
When rendering a template a map of values will be passed to it:
issue_id: The id of the issue
repo: the name of the repository
sender: the handle of the user creating the issue
bot_name: the name of the bot user responding
If the template needs some other value included in the body of the issue, they can be declared using the data_from_issue
param and those values will be passed to the template too, if can be extracted from the body. They can be used in the template using the syntax:
{{variable_name}}
In order to use a template, Buffy will look for the file declared in the template_file
param in the target repo, in the location specified with the template_path
setting (by default .buffy/templates
). In short: the template_file should be located in the template_path.
The values needed by the template that are listed in the data_from_issue
param must be extractable: they have to be enclosed in HTML comments:
<!--<name>--> Info to extract <!--end-<name>-->
So, for example, if you want to use the value of version in the template, the body of the issue must include it inside HTML comments:
<!--version--> v2.1 <!--end-version-->
Then it should be declared in the settings file, listed in the data_from_issue param:
responders:
welcome:
template_file: welcome.md
data_from_issue:
- version
And can then be used in the template:
Thank you for your submission, we will review the {{version}} release of your software.
When invoking an external service:
Some parameters are required for the external call to work: the name
of the service and the url
of the call, both configured in the settings YAML file nested under the external_service
param.
Similarly to the External Service responder if the call is successful the response is posted as a comment in the issue (optionally using a template).
You can configure a template file as a response after the external API call, this template is configured separately from the previous general response template. The response from the external service should be in JSON format. It will be parsed and the resulting hash values will be passed to the template.
Params
For replying with plain text message(s):
- message:
A text message to use as reply.
- messages:
<Array> A list of text messages to respond with.
To reply with a template file:
- template_file:
The name of the template file to use to build the response message.
- data_from_issue:
<Array> An optional list of values that will be extracted from the issue’s body and used to fill the template.
Calling an external service:
- external_service:
All the configuration for the service is nested under this param. Posible options are:
- name:
Required. The name for this service.
- url:
Required. The url to call.
- method:
The HTTP method to use. Valid values: [get, post]. Default is post.
- template_file:
The optional template file to use to build the response message after the external call.
- headers:
<Array> An optional list of key: value pairs to be passed as headers in the external service request.
- data_from_issue:
<Array> An optional list of values that will be extracted from the issue’s body and used to fill the template.
- query_params:
<Array> An optional list of params to add to the query of the external call. Common place to add API_KEYS or other authentication info.
- mapping:
<Array> An optional mapping of variable names in the query of the external service call.
Running other responder(s):
- run_responder:
Allows to call a different responder. Subparams are:
- responder_key:
Required. The key to find the responder in the config file.
- responder_name:
Optional. The name of the responder in the config file if there are several instances under the same responder key.
- message:
Optional. The message to trigger the responder with.
If you want to run multiple responders, use an array of these subparams.
General:
- close:
<Boolean> Optional parameter, if true the responder will close the issue. Default is false.
- check_references:
Optional. If present the validity of the DOIs from the paper’s references file will be checked.
- repo_checks:
Optional. If present the responder will perform (asynchronously) several checks on the target repository. You can configure which checks to perform using nested params. Available options are those of the repository_checks responder
- hidden:
Is true by default.
Examples
Simplest use case:
...
responders:
welcome:
message: "Thanks for your submission!"
...
Multiple messages and a template:
...
responders:
welcome:
messages:
- "You can list all the available commands typing `@botsci help`"
- "The review will start once two reviewers are assigned, please stay tuned."
template_file: welcome.md
data_from_issue:
- repository
- version
...
Calling an external service:
...
responders:
welcome:
external_service:
url: https://dummy-external-service.herokuapp.com/code-analysis
method: post
query_params:
secret: A1234567890Z
data_from_issue:
- target-repo
mapping:
id: issue_id
...
When a new issue is created the responder will send a POST request to https://dummy-external-service.herokuapp.com/code-analysis with a JSON body:
{
"secret": "A1234567890Z", # declared in the query_params setting
"target-repo":"...", # the value is extracted from the body of the issue
"id":"...", # the value corresponds to issue_id, it has been mapped to id
"repo":"...", # the origin repo where the invocation happend
"sender":"...", # the user invoking the command
"bot_name":"...", # the bot user name that will be responding
}
And the response from the external service will posted as a comment in the original issue.
In action
Text messages and template file:
The template file:
In use (template + 2 messages):
Calling an external service:
Goodbye
This responder acts when a issue is closed. It can reply with text messages, using a template or creating a background job to asynchronously call an external service’s API.
Allows labeling.
Listens to
Issue closed event.
Settings key
goodbye
Requirements
When invoking an external service:
Some parameters are required for the external call to work: the name
of the service and the url
of the call, both configured in the settings YAML file nested under the external_service
param.
Similarly to the External Service responder if the call is successful the response is posted as a comment in the issue (optionally using a template).
You can configure a template file as a response after the external API call, this template is configured separately from the previous general response template. The response from the external service should be in JSON format. It will be parsed and the resulting hash values will be passed to the template.
Params
For replying with plain text message(s):
- message:
A text message to use as reply.
- messages:
<Array> A list of text messages to respond with.
To reply with a template file:
- template_file:
The name of the template file to use to build the response message.
- data_from_issue:
<Array> An optional list of values that will be extracted from the issue’s body and used to fill the template.
Calling an external service:
- external_service:
All the configuration for the service is nested under this param. Posible options are:
- name:
Required. The name for this service.
- url:
Required. The url to call.
- method:
The HTTP method to use. Valid values: [get, post]. Default is post.
- template_file:
The optional template file to use to build the response message after the external call.
- headers:
<Array> An optional list of key: value pairs to be passed as headers in the external service request.
- data_from_issue:
<Array> An optional list of values that will be extracted from the issue’s body and used to fill the template.
- query_params:
<Array> An optional list of params to add to the query of the external call. Common place to add API_KEYS or other authentication info.
- mapping:
<Array> An optional mapping of variable names in the query of the external service call.
Examples
Simplest use case:
...
responders:
goodbye:
message: "Congratulations on the acceptance of your paper!"
...
Multiple messages and a template only if label present:
...
responders:
goodbye:
if:
labels:
- accepted
messages:
- "Congratulations on the acceptance of your paper!"
- "Review process finished. Closing the issue."
template_file: goodbye.md
data_from_issue:
- repository
- doi
...
Calling an external service:
...
responders:
goodbye:
external_service:
url: https://dummy-external-service.herokuapp.com/code-analysis
method: post
query_params:
secret: A1234567890Z
data_from_issue:
- target-repo
mapping:
id: issue_id
...
When a new issue is closed the responder will send a POST request to https://dummy-external-service.herokuapp.com/code-analysis with a JSON body:
{
"secret": "A1234567890Z", # declared in the query_params setting
"target-repo":"...", # the value is extracted from the body of the issue
"id":"...", # the value corresponds to issue_id, it has been mapped to id
"repo":"...", # the origin repo where the invocation happend
"sender":"...", # the user invoking the command
"bot_name":"...", # the bot user name that will be responding
}
And the response from the external service will posted as a comment in the original issue.
In action
Text messages and template file:
Replying with a template once the issue is closed:
Close issue command
This responder replies to a specific command closing the issue and possibly adding some labels. Allows labeling.
Listens to
@botname <command>
For example, if you configure the command to be reject, it would respond to:
@botname reject
Settings key
close_issue_command
Params
- command:
The command this responder will listen to.
- description:
Optional String to show when the help command is invoked (if the responder is not hidden).
Examples
Simplest use case:
Just close the issue.
...
responders:
close_issue_command:
command: reject
...
Close issue, add labels, restrict access to editors:
...
responders:
close_issue_command:
only: editors
command: reject
add_labels:
- rejected
...
In action
Update comment
This responder edits the comment triggering the responder updating it with the content of a customizable template file.
These updates of the original comment are useful to add content (for instance: checklists) to be modified/updated by the original author of the comment, as they are always allowed to edit their own comments, not requiring to add them as collaborator of the repository/organization.
Listens to
@botname <command>
For example, if you configure the command to be list pre-acceptance tasks, it would respond to:
@botname list pre-acceptance tasks
Requirements
The response is generated using a template file that should be available in the repository.
Settings key
update_comment
Params
- command:
Required. The command this responder will listen to.
- template_file:
Required. The name of the template file to edit the comment with.
- description:
Optional String to show when the help command is invoked.
Examples
Simplest use case:
...
responders:
update_comment:
command: list tasks
template_file: tasks.md
...
Limiting use to editors team and adding info to use in the template:
...
responders:
add_remove_checklist:
only: editors
command: create pre-acceptance steps checklist
template_file: editor_final_checklist.md
data_from_issue:
- target-repository
- author-handle
...
In action
Invocation:
Comment edited by the bot:
External start review
This responder checks for the presence of editor and reviewers in an issue and then delegates the creation of a new review isuue to an external API call.
Listens to
@botname start review
Requirements
The parameters required for the responder to work are the ones configuring the external API call, nested under the external_call
parameter.
Settings key
external_start_review
Params
- external_call:
Required. Nested under this parameter is the configuration for the external call that will start the review. All available subparams are described in the external_service docs.
- review_title_regex:
Optional. By default the responder will check that this command has not been triggered from a review issue by checking the title. If it starts with [REVIEW]: the command will be rejected. This parameter allows to specify a different string/regex to identify a review issue matching the title.
Examples
Restricted to editors, respond with a template and close the issue:
...
external_start_review:
only: editors
external_call:
url: "https://test.joss.theoj.org/papers/api_start_review"
query_params:
secret: <%= ENV['TEST_SECRET'] %>
mapping:
id: issue_id
editor: editor_login
reviewers: reviewers_logins
silent: true
template_file: "review_started.md"
close: true
...
The responder will call https://test.joss.theoj.org/papers/api_start_review and the response will be passed to the review_started.md template.
In action
External service
This responder creates a background job to asynchronously call an external service’s API. If the call is successful the response is posted as a comment in the issue (optionally using a template).
Listens to
@botname <command>
For example, if you configure the command to be analyze code, it would respond to:
@botname analyze code
Requirements
Some parameters are required for the responder to work: the name
of the service, the command
to invoke it, and the url
of the call. All can be set using the settings YAML file.
If using a template
If you want to use a template to respond, Buffy will look for the file declared in the template_file
param in the target repo, in the location specified with the template_path
setting (by default .buffy/templates
). In short: the template_file should be located in the template_path.
The response from the external service should be in JSON format. It will be parsed and the resulting hash values will be passed to the template, where they can be used with the syntax:
{{variable_name}}
Settings key
external_service
Params
General
- name:
Required. The name for this service.
- command:
Required. The command this responder will listen to.
- description:
The description of the service. It will show in the help command if the responder is not hidden.
- example_invocation:
Optional string to show as an example of the command being used when the help command is invoked.
- message:
An optional message to reply when the command is received, before the external service is called.
Configuring the request
- url:
Required. The url to call.
- method:
The HTTP method to use. Valid values: [get, post]. Default is post.
- headers:
<Array> An optional list of key: value pairs to be passed as headers in the external service request.
- query_params:
<Array> An optional list of params to add to the query of the external call. Common place to add API_KEYS or other authentication info.
- data_from_issue:
<Array> An optional list of values that will be extracted from the issue’s info or issue’s body and sent as query params to the service call. Available info includes: issue_id, issue_author, repo, sender, bot_name, and any variable included in the body of the issue. Also if the command matches any data it will be available as match_data_1, match_data_2, etc.
- mapping:
<Array> An optional mapping of variable names in the query of the external service call.
Configuring the response
- template_file:
The optional template file to use to build the response message if the response from the external service is successful.
- success_msg:
Optional message to respond with if the service call is successful.
- error_msg:
Optional message to respond with if the service call fails with a 400/500 response.
- silent:
<Boolean> Optional parameter, if true the responder won’t reply after the external service is called (template_file, success_msg and error_msg will overwrite this if present). Default is false.
- add_labels:
<Array> Optional parameter. Labels to add to the issue if the external service call is successful.
- remove_labels:
<Array> Optional parameter. Labels to remove from the issue if the external service call is successful.
- close:
<Boolean> Optional parameter, if true the responder will close the issue if the external service call is successful. Default is false.
Examples
A simple case using a template:
...
external_service:
name: cat_facts
command: tell me something about cats
description: Random facts about cats
url: "https://cat-fact.herokuapp.com/facts/random"
method: get
query_params:
animal_type: cat
amount: 1
template_file: cats.md
...
The request will be https://cat-fact.herokuapp.com/facts/random?animal_type=cat&amount=1 and the response will be passed to the cats.md template.
A complete example:
...
external_service:
- code_quality:
only: editors
command: analyze code
description: Reports on the quality of the code
message: Inspecting code...
url: https://dummy-external-service.herokuapp.com/code-analysis
method: post
query_params:
secret: A1234567890Z
data_from_issue:
- target-repo
mapping:
id: issue_id
...
Once the responder is invoked it will reply with “Inspecting code…” as a comment in the issue. Later, a POST request will be sent to https://dummy-external-service.herokuapp.com/code-analysis with a JSON body:
{
"secret": "A1234567890Z", # declared in the query_params setting
"target-repo":"...", # the value is extracted from the body of the issue
"id":"...", # the value corresponds to issue_id, it has been mapped to id
"repo":"...", # the origin repo where the invocation happend
"sender":"...", # the user invoking the command
"bot_name":"...", # the bot user name that will be responding
}
And the response will posted as a comment in the original issue.
In action
In use:
With a template as response
The template file:
The JSON response:
In use:
GitHub Action
This responder triggers workflow run on a GitHub Action using the GitHub API. Optionally if the call is successful (not the result of the workflow run but the call to trigger it) a reply message can be posted as a comment in the issue. Allows labeling.
Listens to
@botname <command>
For example, if you configure the command to be compile pdf, it will respond to:
@botname compile pdf
Requirements
Some parameters are required for the responder to work: the command
to invoke it, and the workflow_repo
and workflow_name
values to identify the action to run. All can be set using the settings YAML file.
Settings key
github_action
Params
- command:
Required. The command this responder will listen to.
- description:
The description of the action this command runs. It will show in the help command if the responder is not hidden.
- example_invocation:
Optional String to show as an example of the command being used when the help command is invoked.
- workflow_repo:
Required. The repo to run the action on, in org/name format.
- workflow_name:
Required. Name of the workflow to run.
- workflow_ref:
Optional. The git ref for the GitHub action to use. Defaults to main.
- message:
An optional message to reply with once the workflow is triggered.
- inputs:
<Map> An optional list of params/values to pass as inputs to the GitHub Action.
- data_from_issue:
<Array> An optional list of fields from the body of the issue to pass as inputs to the GitHub Action.
- mapping:
<Map> An optional mapping of variable names to add to the inputs.
You can use this action to run other responder(s) after after the GitHub action is triggered:
- run_responder:
Allows to call a different responder. Subparams are:
- responder_key:
Required. The key to find the responder in the config file.
- responder_name:
Optional. The name of the responder in the config file if there are several instances under the same responder key.
- message:
Optional. The message to trigger the responder with.
If you want to run multiple responders, use an array of these subparams.
Examples
A complete example:
...
github_action:
only: editors
command: compile pdf
description: Generates a PDF based on the paper.md file in the repository
workflow_repo: openjournals/reviews
workflow_name: compile-pdf.yml
inputs:
file: paper.md
data-from-issue:
- branch
- target_repository
mapping:
repository: target_repository
number: issue_id
...
Once the responder is invoked it triggers the compile-pdf.yml workflow on the openjournals/reviews repository passing to it the file, repository, branch and number inputs.
Wrong command
This is a special responder that replies when Buffy receives a command directed to the bot that no responder understand. By default it replies with:
I'm sorry human, I don't understand that. You can see what commands I support by typing:
@botname help
But the reply can be configured to be a custom message or to use a template.
Listens to
@botname whatever is not a command to other responder
Settings key
If using default reply this responder doesn’t need to be added to the config file. Otherwise:
wrong_command
Params
- ignore:
Optional. If true this responder won’t act. Default value: false.
- template_file:
Optional. A template file to use to build the response message.
- message:
Optional. A text message to use as reply.
Examples
Simplest use case:
Nothing added to the config file, it will reply the default response
...
responders:
...
Deactivate responder:
...
responders:
wrong_command:
ignore: true
...
Use custom message:
...
responders:
wrong_command:
message: "Say what?"
...
In action
Unknown command:
ROpenSci :: Reviewers & due date
This responder can be used to add/remove a user to/from the reviewers list in the body of the issue. It also sets a due date for the review and updates that info in the body of the issue and in the reply comment. This responder will also update Airtable adding entries to the reviewers and reviews tables, and creating if still not present entries in the packages and authors tables. Allows labeling, that will take effect when the second reviewer is assigned.
Listens to
@botname add @username to reviewers
@botname remove @username from reviewers
Requirements
The body of the issue should have a couple of placeholders marked with HTML comments: the reviewers-list and the due-dates-list
<!--reviewers-list--> <!--end-reviewers-list-->
<!--due-dates-list--> <!--end-due-dates-list-->
Settings key
ropensci_reviewers
Params
- due_date_days:
<Integer> Optional. The number of days from the moment a reviewer is assigned to the due date for the review. Default value is 21 (three weeks).
- sample_value:
Optional. A sample value string for the username field. It is used for documentation purposes when the Help responder lists all available responders. Default value is xxxxx.
- no_reviewer_text:
Optional. The text that will go in the removed reviewer place to state there’s no one assigned. Default value is TBD.
- add_as_assignee:
<Boolean> Optional. If true, the new reviewer will be added as assignee to the issue. Default value is false.
- add_as_collaborator:
<Boolean> Optional. If true, the new reviewer it will be added as collaborator to the repo. Default value is false.
- reminder:
Used to configure automatic reminders. See next.
Automatic reminders: To configure an automatic reminder for the reviewers the reminder
param can be used with two nested options under it:
- days_before_deadline:
<Integer> Optional. Configure when the reminder will be posted (how many days before the dealine for the review). Default value: 4
- template_file:
The template file to use for the reminder (will receive variables: reviewer, days_before_deadline and due_date).
For the Airtable connection to work two parameters must be present in the env
section of the settings file, configured using environment variable:
...
env:
airtable_api_key: <%= ENV['AIRTABLE_API_KEY'] %>
airtable_base_id: <%= ENV['AIRTABLE_BASE_ID'] %>
...
Examples
Simplest case:
...
responders:
ropensci_reviewers:
...
With labeling, changing no_reviewer_text, setting a reminder, limiting access and only if there’s an editor already assigned:
...
responders:
ropensci_reviewers:
only:
- editors
if:
role_assigned: editor
no_reviewer_text: "Pending"
add_labels:
- 3/reviewer(s)-assigned
remove_labels:
- 2/seeking-reviewer(s)
reminder:
days_before_deadline: 4
template_file: reminder.md
...
In action
Initial state:
Issue’s body with placeholders
Invocation:
Assigns first reviewer
Assigning second reviewer applies labeling:
Final state:
Issue’s body with reviewers and due dates info
ROpenSci :: Set due date
This responder can be used to add or change the review due date for a current reviewer.
Listens to
@botname set due date for @reviewer to YYYY-MM-DD
Requirements
The body of the issue should have the reviewers-list and the due-dates-list placeholders marked with HTML comments:
<!--reviewers-list--> <!--end-reviewers-list-->
<!--due-dates-list--> <!--end-due-dates-list-->
The reviewer should be already listed in the reviewers list
The format for the due date must be YYYY-MM-DD
The new due date can not be in the past
Settings key
ropensci_set_due_date
Examples
Restricted to editors:
...
responders:
ropensci_set_due_date:
only:
- editors
...
In action
Initial state:
Initial issue’s body
Invocation:
Set new due date for a reviewer
Final state:
Issue’s body with new due date info
ROpenSci :: Seeking reviewers
This responder changes the review to a seeking reviewers
mode, adding and removing appropiate labels and responding with a message with further instructions for authors. This responder will also call Airtable to create an entry in the packages table and entries for all authors (author1 and author-others) in the authors tables.
Listens to
@botname seeking reviewers
Settings key
ropensci_seeking_reviewers
Params
- template_file:
The optional template file to use to build the response message.
- add_labels:
<Array> Optional parameter. Labels to add to the issue.
- remove_labels:
<Array> Optional parameter. Labels to remove from the issue.
As with any responder interacting with Airtable, two parameters must be present in the env
section of the settings file, configured using environment variables:
...
env:
airtable_api_key: <%= ENV['AIRTABLE_API_KEY'] %>
airtable_base_id: <%= ENV['AIRTABLE_BASE_ID'] %>
...
Examples
Restricted to editors:
...
responders:
ropensci_seeking_reviewers:
only:
- editors
template_file: badge.md
remove_labels:
- 1/editor-checks
add_labels:
- 2/seeking-reviewer(s)
...
In action
Run by an editor:
ROpenSci :: Approve
This responder is used to approve a package. It performs a series of tasks:
Adds
date-accepted
to the body of the issueClears reviewers’ current_assignment in AirTable
Creates a new team named like the package-name and invites the creator of the issue to it (owner right needed)
Can reply with a template
Allows labeling
Closes the issue
If the submission-type is
stats
it checks if stasgrade is present and if so adds the proper label
Listens to
@botname approve package-name
Requirements
The package-name must be specified in the command, otherwise an error message will be sent as reply.
If the submission-type of the issue is stats, then for the responder to work there must be a valid value for a statsgrade variable (marked with HTML comments) in the body of the issue:
# the responder will add the label: '6/approved-silver'
<!--statsgrade-->silver<!--end-statsgrade-->
Settings key
ropensci_approve
Params
For the Airtable connection to work two parameters must be present in the env
section of the settings file, configured using environment variable:
...
env:
airtable_api_key: <%= ENV['AIRTABLE_API_KEY'] %>
airtable_base_id: <%= ENV['AIRTABLE_BASE_ID'] %>
...
For labeling the approved stats
submissions an external service is used to get the proper versioned label.
The url for the external service is by default: http://138.68.123.59:8000/stats_badge
. This value can be changed using the optional :stats_badge_url
param:
...
responders:
ropensci_approve:
only: editors
stats_badge_url: https://test.ropensci:3030
...
Examples
Simplest case:
...
responders:
ropensci_approve:
...
With labeling, template response, limiting access and only if there’s an editor already assigned:
...
responders:
ropensci_approve:
only: editors
template_file: approved.md
data_from_issue:
- reviewers-list
remove_labels:
- 5/awaiting-reviewer(s)-response
add_labels:
- 6/approved
...
ROpenSci :: Finalize transfer
This responder is used to assing a recent approved and transfered package to a rOpenSci team. It needs owner rights to work. It performs a series of tasks:
Checks for the presence of the package-name repo in the rOpenSci GitHub organization
Creates a new team named like the package-name and invites the creator of the issue to it, if the team does not exists already.
Adds the package-name repo to the package-name team with admin rights so the members of the team can manage it
Listens to
@botname finalize transfer of package-name
Requirements
The package-name must be specified in the command, otherwise an error message will be sent as reply. The bot must have owner rights.
Settings key
ropensci_finalize_transfer
Example:
...
responders:
ropensci_finalize_transfer:
only: editors
...
ROpenSci :: Mint
This responder mints a submission: it can be used to add a valid badge grade value (currently bronze/silver/gold) to the kind of submissions accepting them (currently stats)
Listens to
@botname mint <grade>
Where <grade> must be a valid value. For example:
@botname mint silver
Requirements
The responder will read the value of submission-type in the body of the issue, for it to work this value must equal stats, then it will update (or add) the value of the statsgrade in the body of the issue.
Settings key
ropensci_mint
Examples
Only available to editors:
...
responders:
ropensci_mint:
only: editors
...
In action
Initial state:
Issue’s body with correct submission type
Invocation:
Final state:
Issue’s body updated with the badge grade
ROpenSci :: Submit review
This responder can be used to update Airtable entries with a review url, duration and date in the reviews table. Once the number of reviews in Airtable equals the number of reviewers in the issue a message will be configured for 12 days later to remind authors to submit their response.
Listens to
@botname submit review <REVIEW_URL> time <REVIEW_HOURS>
Where <REVIEW_URL> must be a valid link to a comment in the issue and <REVIEW_HOURS> is numeric. For example:
@botname submit review https://github.com/ropensci/software-review/issues/338#issuecomment-536199121 time 7.5
Requirements
REVIEW_URL must be a complete url pointing to a comment in the review issue.
REVIEW_HOURS is numeric. Example of valid values: 4
, 10.5
, 7,5
Settings key
ropensci_submit_reviews
Params
- label_when_all_reviews_in:
Optional Labeling to add to the issue once the number of reviews in Airtable equals the number of reviewers in the issue.
- unlabel_when_all_reviews_in:
Optional Labeling to remove from the issue once the number of reviews in Airtable equals the number of reviewers in the issue.
For the Airtable connection to work two parameters must be present in the env
section of the settings file, configured using environment variable:
...
env:
airtable_api_key: <%= ENV['AIRTABLE_API_KEY'] %>
airtable_base_id: <%= ENV['AIRTABLE_BASE_ID'] %>
...
Examples
Simplest case:
...
responders:
ropensci_submit_reviews:
...
With labeling once all reviews are completed and limiting access to editors:
...
responders:
ropensci_submit_reviews:
only:
- editors
label_when_all_reviews_in: "4/review-in-awaiting-changes"
unlabel_when_all_reviews_in: "3/reviewer(s)-assigned"
...
In action
Invocation: Log first review
Logging last review applies labeling:
ROpenSci :: On hold
This responder is used by an editor to put the submission on hold (by default for 90 days, but that is configurable). The responder will label the issue with the holding label and once the time limit is reached the editor will be pinged to review the holding status and possibly close the issue.
Listens to
@botname put on hold
Settings key
ropensci_on_hold
Params
- on_hold_label:
Optional. The label to add to the issue. By default is holding
- on_hold_days:
Integer An optional number of days to have the issue on hold before reminding the editor. Default value is 90.
Example:
Allow the command to be run only by editors, and set reminder to 26 days:
...
responders:
ropensci_on_hold:
only: editors
on_hold_days: 26
...
In action
Labeling
Several Buffy responders allow labeling. A responder allowing labeling means that if the responder finish successfully its main task, it can add and/or remove labels to the issue if they are specified in the settings file.
Settings
Responders allowing labeling will accept in their settings two keys:
- add_labels:
an optional Array of labels to add
- remove_labels:
an optional Array of labels to remove
Example:
...
responders:
example_responder:
add_labels:
- review-finished
- recommend publication
remove_labels:
- pending-review
...
If the example responder is successfull the review-finished
and recommend publication
labels will be added and the pending-review
label will be removed from the issue.
Responders listening to Add/Remove actions
Some responders listen to two opposite add
and remove
actions (for instance the add_remove_assignee responder). In these cases, the add action will process the labeling normally –adding the specified :add_labels
and removing the :remove_labels
– and the remove action will undo that labeling, i.e. removing the :add_labels
and adding the labels from the :remove_labels
setting.
Using templates
Several Buffy responders can reply with a template. Please read each Responder documentation to know if a specific Responder allows this option.
Template files
Templates must be created in the repository using Buffy. Every template is a different file in the repo. To make use of them Buffy needs to know where the templates are located, and the individual name of each template file. As the comments in GitHub issues are rendered using markdown, usually the templates will be plain text or .md files, but that is not mandatory for Buffy to use them.
Location
Buffy will look for the templates in the target repository. By default it will look under the .buffy/templates
dir. This value can be modified in the settings file with the templates_path
setting. If present, the value of this setting will be considered the relative value in the target repo where templates are located.
Name
In the responders allowing templates for replies, the template is specified using the template_file
setting for that responder. Value should be the name of the file including the extension if it has one.
Example
If Buffy is configured to work on a repo with address https://github.com/scientific-journal/astronomy and the settings.yml file has the following value for template_path:
buffy:
templates_path: .templates
...
and you declare a template in a responder using template_file with this value:
...
responders:
welcome_template:
template_file: welcome.md
...
Buffy will use the content of https://github.com/scientific-journal/astronomy/.templates/welcome.md to respond.
Populating templates
The content of a template can include placeholders to be filled with the actual values of a variable. The syntax is:
{{variable_name}}
When rendering a template, Buffy will use a hash of key:value
pairs. When a placeholder is found in the template, it will look up for the corresponding key in the hash and insert the value in the template. The hash will always include at least:
issue_id: The id of the issue
issue_author: The handle of the user that opened the issue
repo: the name of the repository
sender: the handle of the user creating the comment/issue triggering the responder
bot_name: the name of the bot user responding
The hash can also include fields extracted from the body of the issue. To add fields use the data_from_issue
setting. For example, to have the target-repository
and author
values from the issue available in the template this would do:
...
responders:
welcome_template:
template_file: welcome.md
data_from_issue:
- target-repository
- author
...
Check each responder documentation for details on other values available to use in templates.
Creating a custom responder
Buffy will load and make available any responder that is located in the app/responders
directory. The simplest way to organize your responders is to add them in a subfolder inside the responders
dir, defining a module for the custom responders.
During this guide as an example, we’ll create a simple responder to get the time.
Responder structure
A responder is a ruby class containing five elements:
keyname: the handle for the responder in the configuration file
define_listening method: a place to declare what events the responder is listening to
process_message method: the code to perform whatever the responder does
description method: to add a short description of the responder for documenting purposes
example_invocation method: to show users how to invoke the responder
The Responder Ruby class
A responder object is a class inheriting from the Responder class, so you should require the Responder class located in /lib
and create a child class.
When initialized, a responder will have accessor methods for the name of the bot (bot_name
) and for the parameters of the responder coming from the config file (params
).
For our example we add a clock_responder.rb file to the new app/responders/myorganization dir.
It declares the responder class in the myorganization module.
require_relative '../../lib/responder'
module Myorganization
class ClockResponder < Responder
end
end
Keyname
Using keyname
you can define the handle for the responder to be used in the configuration file. Using a symbol is ok.
For our example we’ll just use clock:
require_relative '../../lib/responder'
module Myorganization
class ClockResponder < Responder
keyname :clock
end
end
Now we can use the responder adding it to the config.yml file:
...
responders:
clock:
...
Define listening
The define_listening
method is the place to specify what the responder is listening to.
You can set values for two instance variables here:
@event_action: the action that triggered the event the responder will listen to
@event_regex: (optional) a regular expression the text body of the event (a comment or the body of an issue) should match for the responder to respond
When an event is sent from the reviews repository to Buffy, only responders that match action and regex (if present) will be run.
Event action
If you are listening to creation of issues, @event_action should be
"issues.opened"
.If you are listening to new comments, @event_action should be
"issue_comment.created""
.
Event regex
The @event_regex variable is where the syntax of every specific command is declared. If it is nil
the responder will respond to every event that matches @event_action.
Inside this method you have available the name of the bot in the @botname
instace variable and all the parameters for this responder from the config file in the @params
instance variable.
For our example, we will be listening to comments and we want the command to be “what time is it?”:
require_relative '../../lib/responder'
module Myorganization
class ClockResponder < Responder
keyname :clock
def define_listening
@event_action = "issue_comment.created"
@event_regex = /\A@#{bot_name} what time is it\?\s*\z/i
end
end
end
Mandatory parameters
You can also declare inside this method which parameters are required in the configuration using required_params
. This will create a reader method for every required parameter.
For example, we could make the command for invoking our responder mandatory and declared in the config.yml file instead that in our regex, that way the command for our responder can be changed and be easily configured:
require_relative '../../lib/responder'
module Myorganization
class ClockResponder < Responder
keyname :clock
def define_listening
required_params :command
@event_action = "issue_comment.created"
@event_regex = /\A@#{bot_name} #{command}\s*\z/i
end
end
end
now the command must be added to the config file or the responder will error and not run:
...
responders:
clock:
command: tell me the time
...
But we don’t want to be too strict so, we’ll allow the command to be changed but by default we’ll have one. For that we’ll use an auxiliary instance method:
require_relative '../../lib/responder'
module Myorganization
class ClockResponder < Responder
keyname :clock
def define_listening
@event_action = "issue_comment.created"
@event_regex = /\A@#{bot_name} #{clock_command}\s*\z/i
end
def clock_command
params[:command] || "what time is it\\?"
end
end
end
Process message
The process_message
method will be called if an event reaches Buffy and it matches the action and the regex in the define_listening method.
It accepts a single argument: the message that triggered the call.
This method is the place of all the custom Ruby code needed to perform whatever is the responder does. To interact back with the reviews repository there are several methods available:
respond(message): will post a comment with the specified message string
respond_external_template(template_name, locals): will post a comment using a template and passing it the locals variables
update_body(mark, end_mark, text): will update the body of the issue between marks with the passed text
add_assignee(user): will add the passed user to the issue’s assignees
remove_assignee(user): will remove the passed user from the issue’s assignees
replace_assignee(old_user, new_user): will replace the passed old_user with new_user in the issue’s assignees
process_labeling: will add/remove labels as specified in the responder config params
If you need to access any matched data from the @event_regex you have them available via the match_data
array.
For our example we’ll just reply a comment with the time:
require_relative '../../lib/responder'
module Myorganization
class ClockResponder < Responder
...
def process_message(message)
respond(Time.now.strftime("⏱ The time is %H:%M:%S %Z, today is %d-%m-%Y ⏱"))
end
end
end
Description
Use the description
method to add a short description of what the responder does.
Our example responder replies with the current time:
require_relative '../../lib/responder'
module Myorganization
class ClockResponder < Responder
...
def description
"Get the current time"
end
end
end
Example invocation
To help users understand how to use the responder, use the example_invocation
to add an example of how the responder is triggered.
In our example responder we’ll use the command declared via config or the default one:
require_relative '../../lib/responder'
module Myorganization
class ClockResponder < Responder
...
def example_invocation
"@#{bot_name} #{params[:command] || 'what time is it?'}"
end
end
end
Sample custom responder
The final version of our clock responder (in app/responders/myorganization/clock_responder.rb
):
require_relative '../../lib/responder'
module Myorganization
class ClockResponder < Responder
keyname :clock
def define_listening
@event_action = "issue_comment.created"
@event_regex = /\A@#{bot_name} #{clock_command}\s*\z/i
end
def process_message(message)
respond(Time.now.strftime("⏱ The time is %H:%M:%S %Z, today is %d-%m-%Y ⏱"))
end
def clock_command
params[:command] || "what time is it\\?"
end
def description
"Get the current time"
end
def example_invocation
"@#{bot_name} #{params[:command] || 'what time is it?'}"
end
end
end
Adding its key to the configuration file in the responder settings:
buffy:
responders:
clock:
...
The responder should be available and ready to use:
Tests
Don’t forget to add tests for any new Responder you create. Buffy uses the RSpec test framework.
For our sample responder, we would create spec/responders/myorganization/clock_responder_spec.rb
require_relative "../../spec_helper.rb"
describe Myorganization::ClockResponder do
subject do
described_class
end
describe "listening" do
before { @responder = subject.new({env: {bot_github_user: "testbot"}}, {}) }
it "should listen to new comments" do
expect(@responder.event_action).to eq("issue_comment.created")
end
it "should define regex" do
expect(@responder.event_regex).to match("@testbot what time is it?")
expect(@responder.event_regex).to_not match("@testbot whatever")
end
it "should allow invocation with custom command" do
custom_responder = subject.new({env: {bot_github_user: "testbot"}},
{command: "tell me the time"})
expect(custom_responder.event_regex).to match("@testbot tell me the time")
expect(custom_responder.event_regex).to_not match("@botsci what time is it?")
end
end
describe "#process_message" do
before do
@responder = subject.new({env: {bot_github_user: "botsci"}}, {})
disable_github_calls_for(@responder)
end
it "should respond to github" do
timenow = Time.now
expected_response = timenow.strftime("⏱ The time is %H:%M:%S %Z, today is %d-%m-%Y ⏱")
expect(Time).to receive(:now).and_respond(timenow)
expect(@responder).to receive(:respond).with(expected_response)
@responder.process_message("@testbot what time is it?")
end
end
end
You can find more examples of responder specs in the /spec/responders
directory.