Skip to content

Instantly share code, notes, and snippets.

Last active April 5, 2019 14:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save track0x1/676b6262e0c524830984cf4e42134819 to your computer and use it in GitHub Desktop.
Save track0x1/676b6262e0c524830984cf4e42134819 to your computer and use it in GitHub Desktop.
const yaml = require('js-yaml');
const tasks = require('./tasks'); // An object of supported tasks (functions)
// Helper function that attempts to run a task
function runTask(name, taskData) {
// Ensure valid task provided
if (!tasks[name]) return Promise.reject(`Invalid task name of '${name}' provided.`);
return tasks[name].call(null, taskData);
// This is our lambda function
module.exports = async (event, context, callback) => {
const response = JSON.parse(event.body);
const repoOwner = response.repository.owner.login;
const repoName =;
// Retrieve lambot.yml
// If missing, hook failure (lambot.yml is required)
// Else, load and execute tasks
const pendingTasks = await githubClient.get('config', { repoOwner, repoName })
.then((res) => yaml.safeLoad(Buffer.from(, 'base64')))
.then((config) =>
Object.keys(config.hooks).reduce((acc, hook) => {
// If hook is enabled, attempt to run it and push to pending task queue
if (config.hooks[hook]) {
acc.push(runTask(hook, { githubEvent, response, config: config.hooks[hook] }));
return acc;
}, []))
.catch(() => {
callback(null, respond(500, 'Missing or invalid lambot.yml.'));
// Wait for all tasks to run
await Promise.all(pendingTasks);
return callback(null, respond(200, 'Success'));
// Newly opened pull-requests without labels will receive a friendly notice
async function labelTask({ githubEvent, response }) {
if (githubEvent === 'pull_request' && response.action === 'opened' && response.pull_request.labels.length === 0) {
const repoName =;
const repoOwner = response.repository.owner.login;
const prNumber = response.number;
// Post comment using the GitHub API
await'comment', {
message: '_Hi :wave:, it\'s Lambot!_\n\n' +
'I noticed this pull request has no assigned labels. :cry:\n\n' +
'Please remember to label your pull requests. That helps keep things organized around here. :slightly_smiling_face:'
return Promise.resolve();
# Automatic versioning & publishing
semver: true
# Prefer labels on pull requests; notify if missing.
labels: true
# Run commands on each push and sends a Github status
- echo Installing node modules...
- npm i
- echo Running tests...
- npm test
const BOT_NAME = 'lambot';
// Automatically version and publish repositories that are npm packages
async function semverTask({ githubEvent, response }) {
// Only listen to push events on master (we don't want to version + publish every branch!),
// ignore commits from our bot (to avoid an infinite loop of versioning + publishing)
// ignore [ci skip] commits
if (githubEvent !== 'push' || === BOT_NAME ||
response.head_commit.message.includes('[ci skip]') ||
response.ref !== 'refs/heads/master'
) {
return Promise.resolve();
// get_version and push_changes are helper functions baked into our Docker image
const commands = [
'echo "Installing node modules..."',
'npm ci',
// Determine next version
// Create versioned commit; publish package; push versioned commit & tag
'echo "Bumping version (type: $increment), publishing, and pushing..."',
'npm version $increment',
'npm publish',
// Run the semver utility in our custom docker image
return spawnDockerTask({
GIT_REPO: response.repository.clone_url,
GIT_BRANCH: response.ref,
DOCKER_COMMANDS: commands.join('&&')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment