forked from mirrors/probot
Merge pull request #141 from probot/read-config
Add helper for loading config from a repository
This commit is contained in:
commit
38bf929a92
|
@ -57,6 +57,4 @@ Plugins _should_ allow all settings to customized for each installation.
|
||||||
|
|
||||||
### Store configuration in the repository
|
### Store configuration in the repository
|
||||||
|
|
||||||
Any configuration _should_ be stored in the repository. Unless the plugin is using files from an established convention, the configuration _should_ be stored in the `.github` directory.
|
Any configuration _should_ be stored in the repository. Unless the plugin is using files from an established convention, the configuration _should_ be stored in the `.github` directory. See the [API docs for `context.config`](https://probot.github.io/probot/latest/Context.html#config).
|
||||||
|
|
||||||
For example, the [owners](https://github.com/probot/owners) plugin reads from the `OWNERS` file, which is a convention that existed before the plugin was created, while the [configurer](https://github.com/probot/configurer) plugin reads from `.github/config.yml`.
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
const path = require('path');
|
||||||
|
const yaml = require('js-yaml');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helpers for extracting information from the webhook event, which can be
|
* Helpers for extracting information from the webhook event, which can be
|
||||||
* passed to GitHub API calls.
|
* passed to GitHub API calls.
|
||||||
|
@ -58,6 +61,28 @@ class Context {
|
||||||
get isBot() {
|
get isBot() {
|
||||||
return this.payload.sender.type === 'Bot';
|
return this.payload.sender.type === 'Bot';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the plugin configuration from the given YAML file in the `.github`
|
||||||
|
* directory of the repository.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
*
|
||||||
|
// Load config from .github/myplugin.yml in the repository
|
||||||
|
const config = await context.config('myplugin.yml');
|
||||||
|
|
||||||
|
if(config.close) {
|
||||||
|
context.github.issues.edit(context.issue({state: 'closed'}));
|
||||||
|
}
|
||||||
|
*
|
||||||
|
* @param {string} fileName Name of the YAML file in the `.github` directory
|
||||||
|
* @return {Promise<Object>} Configuration object read from the file
|
||||||
|
*/
|
||||||
|
async config(fileName) {
|
||||||
|
const params = this.repo({path: path.join('.github', fileName)});
|
||||||
|
const res = await this.github.repos.getContent(params);
|
||||||
|
return yaml.safeLoad(Buffer.from(res.data.content, 'base64').toString()) || {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = Context;
|
module.exports = Context;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
"github": "^9.2.0",
|
"github": "^9.2.0",
|
||||||
"github-app": "^3.0.0",
|
"github-app": "^3.0.0",
|
||||||
"github-webhook-handler": "^0.6.0",
|
"github-webhook-handler": "^0.6.0",
|
||||||
|
"js-yaml": "^3.8.4",
|
||||||
"load-plugins": "^2.1.2",
|
"load-plugins": "^2.1.2",
|
||||||
"pkg-conf": "^2.0.0",
|
"pkg-conf": "^2.0.0",
|
||||||
"promise-events": "^0.1.3",
|
"promise-events": "^0.1.3",
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
const expect = require('expect');
|
const expect = require('expect');
|
||||||
const Context = require('../lib/context');
|
const Context = require('../lib/context');
|
||||||
|
|
||||||
|
@ -67,4 +69,96 @@ describe('Context', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('config', function () {
|
||||||
|
let github;
|
||||||
|
|
||||||
|
function readConfig(fileName) {
|
||||||
|
const configPath = path.join(__dirname, 'fixtures', 'config', fileName);
|
||||||
|
const content = fs.readFileSync(configPath, {encoding: 'utf8'});
|
||||||
|
return {data: {content: Buffer.from(content).toString('base64')}};
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
github = {
|
||||||
|
repos: {
|
||||||
|
getContent: expect.createSpy()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
context = new Context(event, github);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('gets a valid configuration', async function () {
|
||||||
|
github.repos.getContent.andReturn(Promise.resolve(readConfig('basic.yml')));
|
||||||
|
const config = await context.config('test-file.yml');
|
||||||
|
|
||||||
|
expect(github.repos.getContent).toHaveBeenCalledWith({
|
||||||
|
owner: 'bkeepers',
|
||||||
|
repo: 'probot',
|
||||||
|
path: '.github/test-file.yml'
|
||||||
|
});
|
||||||
|
expect(config).toEqual({
|
||||||
|
foo: 5,
|
||||||
|
bar: 7,
|
||||||
|
baz: 11
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws when the file is missing', async function () {
|
||||||
|
github.repos.getContent.andReturn(Promise.reject(new Error('An error occurred')));
|
||||||
|
|
||||||
|
let e;
|
||||||
|
let contents;
|
||||||
|
try {
|
||||||
|
contents = await context.config('test-file.yml');
|
||||||
|
} catch (err) {
|
||||||
|
e = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(contents).toNotExist();
|
||||||
|
expect(e).toExist();
|
||||||
|
expect(e.message).toEqual('An error occurred');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws when the configuration file is malformed', async function () {
|
||||||
|
github.repos.getContent.andReturn(Promise.resolve(readConfig('malformed.yml')));
|
||||||
|
|
||||||
|
let e;
|
||||||
|
let contents;
|
||||||
|
try {
|
||||||
|
contents = await context.config('test-file.yml');
|
||||||
|
} catch (err) {
|
||||||
|
e = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(contents).toNotExist();
|
||||||
|
expect(e).toExist();
|
||||||
|
expect(e.message).toMatch(/^end of the stream or a document separator/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('throws when loading unsafe yaml', async function () {
|
||||||
|
github.repos.getContent.andReturn(readConfig('evil.yml'));
|
||||||
|
|
||||||
|
let e;
|
||||||
|
let config;
|
||||||
|
try {
|
||||||
|
config = await context.config('evil.yml');
|
||||||
|
} catch (err) {
|
||||||
|
e = err;
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(config).toNotExist();
|
||||||
|
expect(e).toExist();
|
||||||
|
expect(e.message).toMatch(/unknown tag/);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('returns an empty object when the file is empty', async function () {
|
||||||
|
github.repos.getContent.andReturn(readConfig('empty.yml'));
|
||||||
|
|
||||||
|
const contents = await context.config('test-file.yml');
|
||||||
|
|
||||||
|
expect(contents).toEqual({});
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
foo: 5
|
||||||
|
bar: 7
|
||||||
|
baz: 11
|
|
@ -0,0 +1 @@
|
||||||
|
evil: !<tag:yaml.org,2002:js/function> 'function () {}'
|
|
@ -0,0 +1 @@
|
||||||
|
@
|
Loading…
Reference in New Issue