<commit_before>// https://github.com/kriasoft/react-starter-kit/blob/master/docs/recipes/how-to-implement-routing.md

/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-present Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */
import moment from 'moment';
import path from 'path';
import express from 'express';
import cookieParser from 'cookie-parser';
import bodyParser from 'body-parser';
import expressJwt from 'express-jwt';
import expressGraphQL from 'express-graphql';
import jwt from 'jsonwebtoken';
import PrettyError from 'pretty-error';
import _httpErrorPages from 'http-error-pages';
import ical from 'ical-generator';

import schema from './data/schema';

import { handleCalendarWebhook, handleTwilioWebhook } from './hooks';
// import { logLogin } from './data/database/login';
import { reactMiddleware, reactErrorMiddleware } from './reactMiddleware';
import API from './api/';

import config from './config';

const PEOPLE_PREFIX = 'people/';

const app = express();

//
// Tell any CSS tooling (such as Material UI) to use all vendor prefixes if the
// user agent is not known.
// -----------------------------------------------------------------------------
global.navigator = global.navigator || {};
global.navigator.userAgent = global.navigator.userAgent || 'all';

//
// Register Node.js middleware
// -----------------------------------------------------------------------------
app.use(express.static(path.resolve(__dirname, 'public')));
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

//
// Authentication
// -----------------------------------------------------------------------------
function checkingUser(req, _payload, done) {
  const secret = config.auth.jwt.secret;
  // req.payload = { foo: 'bar' };
  // console.log(`checking req.url`, req.url);
  // console.log(`payload`, payload);
  // if (payload.data) {
  //   logLogin(payload.data.username, payload);
  // }
  done(null, secret);
}

function populatePayload(req, _res, next) {
  const token = req.cookies.token;
  if (token) {
    const decoded = jwt.verify(token, config.auth.jwt.secret);
    req.payload = decoded;
  }
  return next();
}

// super temporary lin #######
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'http://*.soho.sg');
  if (req.method === 'OPTIONS') {
    res.header('Access-Control-Allow-Methods', 'PUT, POST, PATCH, DELETE, GET');
    return res.status(200).json({});
  }
  return next();
});

/* bot denial */
app.use((req, res, next) => {
  if (req.headers.from === 'googlebot(at)googlebot.com') {
    return res.status(401).json({
      message: 'Unathorized Access',
    });
  }
  return next();
});

/* to populate cross cookies, tech debt */
app.use((req, res, next) => {
  if (req.query.token) {
    // console.log('req.query.token', req.query.token);
    const expiresIn = 60 * 60 * 24 * 180; // 180 days
    res.cookie('token', req.query.token, {
      maxAge: 1000 * expiresIn,
      httpOnly: true,
    });
    /* tech debt */
    res.cookie('jwt', req.query.token, {
      maxAge: 1000 * expiresIn,
      httpOnly: true,
    });
    return next();
  }
  return next();
});

app.use(
  expressJwt({
    secret: checkingUser,
    credentialsRequired: true,
    getToken: function fromHeaderOrQuerystring(req) {
      // console.log(`Object.keys(req)`, Object.keys(req));
      if (req.cookies.token) {
        return req.cookies.token;
      } else if (req.query && req.query.token) {
        return req.query.token;
      }
      return null;
    },
  }).unless({
    path: [
      '/graphql',
      '/events/calendar',
      /\/general*/,
      /\/assets*/,
      /\/page+/,
      /\/p+/,
      /\/api+/,
      /\/webhooks+/,
    ],
  }),
);
// Error handler for express-jwt
app.use(populatePayload);
app.use(reactErrorMiddleware);

//
// Register API middleware
// -----------------------------------------------------------------------------
app.use(
  '/graphql',
  expressGraphQL(async req => ({
    schema,
    graphiql: __DEV__,
    rootValue: { request: req },
    pretty: __DEV__,
  })),
);

app.use('/events/calendar', async (req, res, next) => {
  // console.error('handleCalendarWebhook typeof', typeof handleCalendarWebhook);
  try {
    await handleCalendarWebhook(req.headers);
    res.json({ ok: true });
  } catch (e) {
    console.error(`handleCalendarWebhook`, e);
    next(e);
  }
});

app.use('/webhooks/twilio', async (req, res) => {
  handleTwilioWebhook(req);
  res.send('null');
  res.status(201).end();
});

app.use('/public/appointment/confirm/:eventId', async (req, res) => {
  const { eventId } = req.params;
  await API({ action: 'patchEvent', status: 'confirmed', eventId });
  res.send(
    `<!doctype html><html><body style="background-color:#373277"><h1><center><span style="color:white">Your Appointment is confirmed!</span></center></h1></body></html>`,
  );
});

app.use('/general/confirmation/:eventId', async (req, res, next) => {
  const { eventId } = req.params;

  if (eventId === 'images') return;

  const event = await API({ action: 'getEvent', eventId });
  req.data = {
    event,
    workAddress: config.app.workAddress,
    oldWorkAddress: config.app.oldWorkAddress,
    safeEntryLink: config.app.safeEntryLink,
    oldSafeEntryLink: config.app.oldSafeEntryLink,
  };
  await API({
    action: 'patchEvent',
    status: 'confirmed',
    confirmed: moment().format('lll'),
    eventId,
  });

  reactMiddleware(req, res, next);
});

app.use('/general/reservation/:eventId', async (req, res, next) => {
  const { eventId } = req.params;

  if (eventId === 'images') return;
  const event = await API({ action: 'getEvent', eventId });
  req.data = {
    event,
    workAddress: config.app.workAddress,
    oldWorkAddress: config.app.oldWorkAddress,
    safeEntryLink: config.app.safeEntryLink,
    oldSafeEntryLink: config.app.oldSafeEntryLink,
  };

  const now = moment();
  const appointmentEnd = moment(event.end.dateTime);
  // eslint-disable-next-line consistent-return
  if (now.isAfter(appointmentEnd)) {
    res.redirect('/');
  } else {
    reactMiddleware(req, res, next);
  }
});

app.use('/general/calendar/:eventId', async (req, res) => {
  const { eventId } = req.params;

  if (eventId === 'images') return;
  const event = await API({ action: 'getEvent', eventId });

  const customerId = event.extendedProperties.shared.resourceName;

  const cal = ical({
    domain: config.app.workDomain,
    name: config.app.workCalendar,
  });

  cal.createEvent({
    start: event.start.dateTime,
    end: event.end.dateTime,
    summary: 'Rare Beauty Appointment',
    description: `
We do Lash Extensions, Facial, Waxing, Threading and More

Appointment(s) are found @ ${config.app.customerURL}/${customerId.substring(
      PEOPLE_PREFIX.length,
      customerId.length,
    )}/appointments
    `,
    location: config.app.workAddress,
    url:
      (event.extendedProperties && event.extendedProperties.shared.shortURL) ||
      config.app.workDomain,
  });

  // eslint-disable-next-line consistent-return
  return cal.serve(res);
  // next(req, res, next);
});

//
// Register server-side rendering middleware
// -----------------------------------------------------------------------------
app.use('*', reactMiddleware);

//
// Error handling
// -----------------------------------------------------------------------------
if (__DEV__) {
  const pe = new PrettyError();
  pe.skipNodeFiles();
  pe.skipPackage('express');

  // eslint-disable-next-line no-unused-vars
  app.use(reactErrorMiddleware);
} else {
  _httpErrorPages(app);
}

//
// Launch the server
// -----------------------------------------------------------------------------
if (!module.hot) {
  app.listen(config.port, () => {
    console.info(`The server is running at http://localhost:${config.port}/`);
  });
} else {
  // development mode
  app.hot = module.hot;
  module.hot.accept('./router');
}

export default app;
<commit_msg>fixing security issue<commit_after>// https://github.com/kriasoft/react-starter-kit/blob/master/docs/recipes/how-to-implement-routing.md

/**
 * React Starter Kit (https://www.reactstarterkit.com/)
 *
 * Copyright © 2014-present Kriasoft, LLC. All rights reserved.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE.txt file in the root directory of this source tree.
 */
import moment from 'moment';
import path from 'path';
import express from 'express';
import cookieParser from 'cookie-parser';
import bodyParser from 'body-parser';
import expressJwt from 'express-jwt';
import expressGraphQL from 'express-graphql';
import jwt from 'jsonwebtoken';
import PrettyError from 'pretty-error';
import _httpErrorPages from 'http-error-pages';
import ical from 'ical-generator';

import schema from './data/schema';

import { handleCalendarWebhook, handleTwilioWebhook } from './hooks';
// import { logLogin } from './data/database/login';
import { reactMiddleware, reactErrorMiddleware } from './reactMiddleware';
import API from './api/';

import config from './config';

const PEOPLE_PREFIX = 'people/';

const app = express();

//
// Tell any CSS tooling (such as Material UI) to use all vendor prefixes if the
// user agent is not known.
// -----------------------------------------------------------------------------
global.navigator = global.navigator || {};
global.navigator.userAgent = global.navigator.userAgent || 'all';

//
// Register Node.js middleware
// -----------------------------------------------------------------------------
app.use(express.static(path.resolve(__dirname, 'public')));
app.use(cookieParser());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

//
// Authentication
// -----------------------------------------------------------------------------
function checkingUser(req, _payload, done) {
  const secret = config.auth.jwt.secret;
  // req.payload = { foo: 'bar' };
  // console.log(`checking req.url`, req.url);
  // console.log(`payload`, payload);
  // if (payload.data) {
  //   logLogin(payload.data.username, payload);
  // }
  done(null, secret);
}

function populatePayload(req, _res, next) {
  const token = req.cookies.token;
  if (token) {
    const decoded = jwt.verify(token, config.auth.jwt.secret);
    req.payload = decoded;
  }
  return next();
}

// super temporary lin #######
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'http://*.soho.sg');
  if (req.method === 'OPTIONS') {
    res.header('Access-Control-Allow-Methods', 'PUT, POST, PATCH, DELETE, GET');
    return res.status(200).json({});
  }
  return next();
});

/* bot denial */
app.use((req, res, next) => {
  if (req.headers.from === 'googlebot(at)googlebot.com') {
    return res.status(401).json({
      message: 'Unathorized Access',
    });
  }
  return next();
});

/* to populate cross cookies, tech debt */
app.use((req, res, next) => {
  if (req.query.token) {
    // console.log('req.query.token', req.query.token);
    const expiresIn = 60 * 60 * 24 * 180; // 180 days
    res.cookie('token', req.query.token, {
      maxAge: 1000 * expiresIn,
      httpOnly: true,
    });
    /* tech debt */
    res.cookie('jwt', req.query.token, {
      maxAge: 1000 * expiresIn,
      httpOnly: true,
    });
    return next();
  }
  return next();
});

app.use(
  expressJwt({
    secret: checkingUser,
    credentialsRequired: true,
    getToken: function fromHeaderOrQuerystring(req) {
      // console.log(`Object.keys(req)`, Object.keys(req));
      // console.log(`req.cookies`, JSON.stringify(req.cookies, null, 2));
      if (req.cookies.token) {
        return req.cookies.token;
      } else if (req.query && req.query.token) {
        return req.query.token;
      } else if (req.headers && req.headers.authorization) {
        const base64Credentials = req.headers.authorization.split(' ')[1];
        // console.log('base64Credentials', base64Credentials);
        return base64Credentials;
      }
      return null;
    },
  }).unless({
    path: [
      '/events/calendar',
      /\/general*/,
      /\/assets*/,
      /\/page+/,
      /\/p+/,
      /\/api+/,
      /\/webhooks+/,
    ],
  }),
);
// Error handler for express-jwt
app.use(populatePayload);
app.use(reactErrorMiddleware);

//
// Register API middleware
// -----------------------------------------------------------------------------
app.use(
  '/graphql',
  expressGraphQL(async req => ({
    schema,
    graphiql: __DEV__,
    rootValue: { request: req },
    pretty: __DEV__,
  })),
);

app.use('/events/calendar', async (req, res, next) => {
  // console.error('handleCalendarWebhook typeof', typeof handleCalendarWebhook);
  try {
    await handleCalendarWebhook(req.headers);
    res.json({ ok: true });
  } catch (e) {
    console.error(`handleCalendarWebhook`, e);
    next(e);
  }
});

app.use('/webhooks/twilio', async (req, res) => {
  handleTwilioWebhook(req);
  res.send('null');
  res.status(201).end();
});

app.use('/public/appointment/confirm/:eventId', async (req, res) => {
  const { eventId } = req.params;
  await API({ action: 'patchEvent', status: 'confirmed', eventId });
  res.send(
    `<!doctype html><html><body style="background-color:#373277"><h1><center><span style="color:white">Your Appointment is confirmed!</span></center></h1></body></html>`,
  );
});

app.use('/general/confirmation/:eventId', async (req, res, next) => {
  const { eventId } = req.params;

  if (eventId === 'images') return;

  const event = await API({ action: 'getEvent', eventId });
  req.data = {
    event,
    workAddress: config.app.workAddress,
    oldWorkAddress: config.app.oldWorkAddress,
    safeEntryLink: config.app.safeEntryLink,
    oldSafeEntryLink: config.app.oldSafeEntryLink,
  };
  await API({
    action: 'patchEvent',
    status: 'confirmed',
    confirmed: moment().format('lll'),
    eventId,
  });

  reactMiddleware(req, res, next);
});

app.use('/general/reservation/:eventId', async (req, res, next) => {
  const { eventId } = req.params;

  if (eventId === 'images') return;
  const event = await API({ action: 'getEvent', eventId });
  req.data = {
    event,
    workAddress: config.app.workAddress,
    oldWorkAddress: config.app.oldWorkAddress,
    safeEntryLink: config.app.safeEntryLink,
    oldSafeEntryLink: config.app.oldSafeEntryLink,
  };

  const now = moment();
  const appointmentEnd = moment(event.end.dateTime);
  // eslint-disable-next-line consistent-return
  if (now.isAfter(appointmentEnd)) {
    res.redirect('/');
  } else {
    reactMiddleware(req, res, next);
  }
});

app.use('/general/calendar/:eventId', async (req, res) => {
  const { eventId } = req.params;

  if (eventId === 'images') return;
  const event = await API({ action: 'getEvent', eventId });

  const customerId = event.extendedProperties.shared.resourceName;

  const cal = ical({
    domain: config.app.workDomain,
    name: config.app.workCalendar,
  });

  cal.createEvent({
    start: event.start.dateTime,
    end: event.end.dateTime,
    summary: 'Rare Beauty Appointment',
    description: `
We do Lash Extensions, Facial, Waxing, Threading and More

Appointment(s) are found @ ${config.app.customerURL}/${customerId.substring(
      PEOPLE_PREFIX.length,
      customerId.length,
    )}/appointments
    `,
    location: config.app.workAddress,
    url:
      (event.extendedProperties && event.extendedProperties.shared.shortURL) ||
      config.app.workDomain,
  });

  // eslint-disable-next-line consistent-return
  return cal.serve(res);
  // next(req, res, next);
});

//
// Register server-side rendering middleware
// -----------------------------------------------------------------------------
app.use('*', reactMiddleware);

//
// Error handling
// -----------------------------------------------------------------------------
if (__DEV__) {
  const pe = new PrettyError();
  pe.skipNodeFiles();
  pe.skipPackage('express');

  // eslint-disable-next-line no-unused-vars
  app.use(reactErrorMiddleware);
} else {
  _httpErrorPages(app);
}

//
// Launch the server
// -----------------------------------------------------------------------------
if (!module.hot) {
  app.listen(config.port, () => {
    console.info(`The server is running at http://localhost:${config.port}/`);
  });
} else {
  // development mode
  app.hot = module.hot;
  module.hot.accept('./router');
}

export default app;
<|endoftext|>


<commit_before>'use-strict'

var hookshot = require('hookshot'),
	fs         = require('fs'),
	sh         = require('execSync'),
	request    = require('request'),
	colors     = require('colors');

var config      = require('../config.json'),
		sh_commands = require('./sh-commands.js');

function verifyAccount(incoming_repo){
	if (incoming_repo == config.github_listener.account_name) return true;
	return false;
}
function checkIfCommitterIsDeployer(members, committer){
	members.some(function(member){
		return member.name === committer;
	})
}
function verifyCommitter(last_commit, cb){
	// You only need to verify the deployer if you're using teams, otherwise disable it and always allow anyone pushing to that repo to deploy.
	if (!config.verify_committer.enabled){
		cb(true);
	}else{
		var committer = last_commit.committer.username;
		request('https://api.github.com/teams/' + config.verify_committer.team_id + '/members?access_token=' + config.verify_committer.access_token, function (error, response, body) {
		  if (!error) {
		  	var committer_is_deployer = checkIfCommitterIsDeployer(JSON.parse(body), committer);
		    cb(committer_is_deployer);
		  } else {
		  	console.log('Error verifying committer'.red, JSON.stringify(error))
		  }
		})
	}
}

function createDirGitInit(info){
	var repo_name = info.repository.name;

	fs.mkdirSync('./repositories/' + repo_name);

	var remote_url_arr = info.repository.url.split('//');
	var authenticated_remote_url = url_arr[0] + '//' + config.verify_committer.access_token + '@' + remote_url_arr[1];
	var create_statement = sh_commands.createGitRepoAndRemotes(repo_name, authenticated_remote_url);
  sh.run(create_statement);
}
function pullLatest(info){
	var repo_name = info.repository.name,
		branch_name = info.ref.split('/')[2], // `ref: "refs/heads/<branchname>` => `branchname`
		delete_branch;

	if (!fs.existsSync('./repositories/' + repo_name)){
		createDirGitInit(info);
	}

	// Download latest data
	var fetch_statement = sh_commands.fetchLatest(repo_name);
	sh.run(fetch_statement);

	// If it's deleted, delete it!
	if (info.deleted) {
		delete_branch = sh_commands.deleteBranch(repo_name, branch_name);
		sh.run(delete_branch);
	}

	// Update all branches
	var track_all_branches = sh_commands.trackAllBranches(repo_name);
	sh.run(track_all_branches);

	// Put the working tree back on to master
	var checkout_master = sh_commands.checkoutMaster(repo_name);
	sh.run(checkout_master);
}
function checkForDeployMsg(last_commit){
	var commit_msg = last_commit.message,
	    cp_deploy_regx   = new RegExp(config.s3.hard_deploy.trigger),
	    sync_deploy_regx = new RegExp(config.s3.sync_deploy_trigger);

	if (config.s3.hard_deploy.enabled && cp_deploy_regx.exec(commit_msg)) return 'cp';
	if (sync_deploy_regx.exec(commit_msg)) return 'sync';
	return false;
}
function deployToS3(deploy_type, most_recent_commit){
	var repo_name   = info.repository.name,
			local_path  = most_recent_commit.split('::')[1], // `published::output` -> `output` or `published::` -> `''`
	    remote_path = config.s3.path || '';

	// If a local path was specified, put a slash in front of it
	if (local_path) local_path = '/' + local_path;

	var deploy_statement = sh_commands.deploy(deploy_type, repo_name, config.s3.bucket_name, local_path, remote_path, config.s3.exclude);
	var deploy_result = sh.exec(deploy_statement);
	// Log deployment result
	console.log(deploy_result.stdout);
}

hookshot(function(info){
	// Is this request coming from the specified GitHub Account?
	var is_account_verified = verifyAccount(info.repository.owner.name);
	// Is there a deploy message present?
	var most_recent_commit,
			deploy_status;
	// The last commit in the array is the most recent
	// But `info.commits` will be an empty array if you pushed a new branch with no commits
	if (info.commits.length) {
		most_recent_commit  = info.commits[info.commits.length - 1];
		deploy_status       = checkForDeployMsg(most_recent_commit);
	}
	// Is this coming from the whitelisted GitHub account?
	if (is_account_verified){
		pullLatest(info);

		// Are we deploying? Has that option been enabled and does the commit have the appropriate message?
		if (config.s3.enabled && deploy_status){
			verifyCommitter(most_recent_commit, function(committer_approved){

				// Does the committer have deploy? privileges?
				if (committer_approved) {
					deployToS3(deploy_status, most_recent_commit);
				} else {
					console.log('Unapproved committer attempted deployment.'.red)
				}

			});
		}
	}
}).listen(config.github_listener.port);

console.log('Listening on port... ' + config.github_listener.port);	<commit_msg>add user agent to github api request<commit_after>'use-strict'

var hookshot = require('hookshot'),
	fs         = require('fs'),
	sh         = require('execSync'),
	request    = require('request'),
	colors     = require('colors');

var config      = require('../config.json'),
		sh_commands = require('./sh-commands.js');

function verifyAccount(incoming_repo){
	if (incoming_repo == config.github_listener.account_name) return true;
	return false;
}
function checkIfCommitterIsDeployer(members, committer){
	members.some(function(member){
		return member.name === committer;
	})
}
function verifyCommitter(last_commit, cb){
	// You only need to verify the deployer if you're using teams, otherwise disable it and always allow anyone pushing to that repo to deploy.
	if (!config.verify_committer.enabled){
		cb(true);
	}else{
		var committer = last_commit.committer.username;
		request({
			url: 'https://api.github.com/teams/' + config.verify_committer.team_id + '/members?access_token=' + config.verify_committer.access_token,
			headers: : {
        'User-Agent': 'Kestrel-publisher'
    		}
    	}, function (error, response, body) {
		  if (!error) {
		  	var committer_is_deployer = checkIfCommitterIsDeployer(JSON.parse(body), committer);
		    cb(committer_is_deployer);
		  } else {
		  	console.log('Error verifying committer'.red, JSON.stringify(error))
		  }
		})
	}
}

function createDirGitInit(info){
	var repo_name = info.repository.name;

	fs.mkdirSync('./repositories/' + repo_name);

	var remote_url_arr = info.repository.url.split('//');
	var authenticated_remote_url = url_arr[0] + '//' + config.verify_committer.access_token + '@' + remote_url_arr[1];
	var create_statement = sh_commands.createGitRepoAndRemotes(repo_name, authenticated_remote_url);
  sh.run(create_statement);
}
function pullLatest(info){
	var repo_name = info.repository.name,
		branch_name = info.ref.split('/')[2], // `ref: "refs/heads/<branchname>` => `branchname`
		delete_branch;

	if (!fs.existsSync('./repositories/' + repo_name)){
		createDirGitInit(info);
	}

	// Download latest data
	var fetch_statement = sh_commands.fetchLatest(repo_name);
	sh.run(fetch_statement);

	// If it's deleted, delete it!
	if (info.deleted) {
		delete_branch = sh_commands.deleteBranch(repo_name, branch_name);
		sh.run(delete_branch);
	}

	// Update all branches
	var track_all_branches = sh_commands.trackAllBranches(repo_name);
	sh.run(track_all_branches);

	// Put the working tree back on to master
	var checkout_master = sh_commands.checkoutMaster(repo_name);
	sh.run(checkout_master);
}
function checkForDeployMsg(last_commit){
	var commit_msg = last_commit.message,
	    cp_deploy_regx   = new RegExp(config.s3.hard_deploy.trigger),
	    sync_deploy_regx = new RegExp(config.s3.sync_deploy_trigger);

	if (config.s3.hard_deploy.enabled && cp_deploy_regx.exec(commit_msg)) return 'cp';
	if (sync_deploy_regx.exec(commit_msg)) return 'sync';
	return false;
}
function deployToS3(deploy_type, most_recent_commit){
	var repo_name   = info.repository.name,
			local_path  = most_recent_commit.split('::')[1], // `published::output` -> `output` or `published::` -> `''`
	    remote_path = config.s3.path || '';

	// If a local path was specified, put a slash in front of it
	if (local_path) local_path = '/' + local_path;

	var deploy_statement = sh_commands.deploy(deploy_type, repo_name, config.s3.bucket_name, local_path, remote_path, config.s3.exclude);
	var deploy_result = sh.exec(deploy_statement);
	// Log deployment result
	console.log(deploy_result.stdout);
}

hookshot(function(info){
	// Is this request coming from the specified GitHub Account?
	var is_account_verified = verifyAccount(info.repository.owner.name);
	// Is there a deploy message present?
	var most_recent_commit,
			deploy_status;
	// The last commit in the array is the most recent
	// But `info.commits` will be an empty array if you pushed a new branch with no commits
	if (info.commits.length) {
		most_recent_commit  = info.commits[info.commits.length - 1];
		deploy_status       = checkForDeployMsg(most_recent_commit);
	}
	// Is this coming from the whitelisted GitHub account?
	if (is_account_verified){
		pullLatest(info);

		// Are we deploying? Has that option been enabled and does the commit have the appropriate message?
		if (config.s3.enabled && deploy_status){
			verifyCommitter(most_recent_commit, function(committer_approved){

				// Does the committer have deploy? privileges?
				if (committer_approved) {
					deployToS3(deploy_status, most_recent_commit);
				} else {
					console.log('Unapproved committer attempted deployment.'.red)
				}

			});
		}
	}
}).listen(config.github_listener.port);

console.log('Listening on port... ' + config.github_listener.port);	<|endoftext|>


<commit_before>// modules
import express from 'express'
import BodyParser from 'body-parser'
import http from 'http'
import _ from 'underscore'

// routes
// import index from './routes/index'

import rpio from 'rpio'

const pins = {
	'drive': {
		'forward': 12,
		'backward': 18
	},
	'steer': {
		'left': 11,
		'right': 13
	}
}

// open gpio pins
rpio.open(12, rpio.OUTPUT, rpio.LOW)
rpio.open(18, rpio.OUTPUT, rpio.LOW)

// sockets
import socketio from 'socket.io'

const app = express()

app.use(BodyParser.json())

//enable cors
app.all('*', function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS')
  res.header('Access-Control-Allow-Headers', 'Content-Type')
  next()
})

app.use('/public', express.static(__dirname + '/public'))

// app.use('/', index);

//setup io
var server = http.createServer(app)

var sio = socketio(server)

// io.on('connection', socketioJwt.authorize({
// 	secret: db.secret,
// 	handshae: true
// }))
sio.on('connection', function (socket) {
	/*
		data:
			direction (up, down)
			speed (1, 0)
	 */
	socket.on('drive', function (data) {
		const speed = (data['speed']) ? rpio.HIGH : rpio.LOW
		const pin = pins.drive[data['direction']]

		rpio.write(pin, speed)
	})

	socket.on('steer', function (data) {
		const speed = (data['speed']) ? rpio.HIGH : rpio.LOW
		const pin = pins.drive[data['direction']]

		rpio.write(pin, speed)
	})

	setInterval(function () {
		socket.emit('helloworld', 'hello world')
	}, 2000)
})

server.listen(3000, function () {
	console.log('app is listening on port 3000')
})

export default app
<commit_msg>add steering<commit_after>// modules
import express from 'express'
import BodyParser from 'body-parser'
import http from 'http'
import _ from 'underscore'

// routes
// import index from './routes/index'

import rpio from 'rpio'

const pins = {
	'drive': {
		'forward': 12,
		'backward': 18,
		'left': 11,
		'right': 13
	},
	'steer': {
		'left': 11,
		'right': 13
	}
}

// open gpio pins
rpio.open(12, rpio.OUTPUT, rpio.LOW)
rpio.open(18, rpio.OUTPUT, rpio.LOW)

// sockets
import socketio from 'socket.io'

const app = express()

app.use(BodyParser.json())

//enable cors
app.all('*', function(req, res, next) {
  res.header('Access-Control-Allow-Origin', '*')
  res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS')
  res.header('Access-Control-Allow-Headers', 'Content-Type')
  next()
})

app.use('/public', express.static(__dirname + '/public'))

// app.use('/', index);

//setup io
var server = http.createServer(app)

var sio = socketio(server)

// io.on('connection', socketioJwt.authorize({
// 	secret: db.secret,
// 	handshae: true
// }))
sio.on('connection', function (socket) {
	/*
		data:
			direction (up, down)
			speed (1, 0)
	 */
	socket.on('drive', function (data) {
		const speed = (data['speed']) ? rpio.HIGH : rpio.LOW
		const pin = pins.drive[data['direction']]

		rpio.write(pin, speed)
	})

	setInterval(function () {
		socket.emit('helloworld', 'hello world')
	}, 2000)
})

server.listen(3000, function () {
	console.log('app is listening on port 3000')
})

export default app
<|endoftext|>


<commit_before>import raven from 'raven'
import newrelic from 'newrelic'
import Express from 'express'
import ReactDOMServer from 'react-dom/server'
import React from 'react'
import Location from 'react-router/lib/Location'
import config from './config'
import favicon from 'serve-favicon'
import compression from 'compression'
import helmet from 'helmet'
import path from 'path'
import createStore from './redux/create'
import authApi from './api/api'
import ApiClient from './ApiClient'
import universalRouter from './universalRouter'
import Html from './Html'
import responseTime from 'response-time'
import winston from 'winston'
import expressWinston from 'express-winston'
import NewrelicWinston from 'newrelic-winston'
import proxy from 'http-proxy-middleware'

const app = new Express()

app.use(responseTime())
app.use(compression())
app.use(helmet())
app.use(favicon(path.join(__dirname, '..', 'static', 'favicon.ico')))
app.use(require('serve-static')(path.join(__dirname, '..', 'static')))

app.use('/auth', proxy({
  target: `http://localhost:${config.apiPort}`,
  pathRewrite: { '^/auth': '' }
}))

app.get(/\/users|\/pt\/users|\/membros/, function(req, res) {
  res.redirect('/')
})

app.get('/robots.txt', function(req, res) {
  res.send('User-Agent: * \n' +
    'Allow: /')
})

if ( (app.get('env') === 'production') || (app.get('env') === 'staging') ) {
  // The error handler must be before any other error middleware
  app.use(raven.middleware.express.errorHandler(process.env.SENTRY_DSN))
}

app.use((req, res, next) => {
  const host = req.headers.host
  const isAppDomain = host === process.env.APP_DOMAIN
  if (isAppDomain) {
    res.redirect(301, `${req.protocol}://app.${host}${req.originalUrl}`)
    return
  }
  next()
})

app.use((req, res, next) => {
  const host = req.headers.host
  const isAppSubdomain = host.indexOf(`app.${process.env.APP_DOMAIN}`) !== -1
  const www = host.match(/^www\.(.*)/)
  const domains = require('fs').readFileSync('./src/redirect.blacklist')
  const lines = domains.toString().split('\n')
  const blacklist = lines.some(line => { if (line) return host.match(line) })

  if (!isAppSubdomain && !__DISABLE_SSR__ && !www && !blacklist) {
    res.redirect(301, `${req.protocol}://www.${host}`)
    return
  }
  else if (!isAppSubdomain && !__DISABLE_SSR__ && www && blacklist) {
    res.redirect(301, `${req.protocol}://${www[1]}`)
    return
  }
  next()
})

app.use((req, res) => {
  if (__DEVELOPMENT__) {
    // Do not cache webpack stats: the script file would change since
    // hot module replacement is enabled in the development env
    webpackIsomorphicTools.refresh()
  }

  const client = new ApiClient(req)
  const store = createStore(client)
  const location = new Location(req.path, req.query)
  if (__DISABLE_SSR__) {
    res.send('<!doctype html>\n' +
      ReactDOMServer.renderToString(<Html assets={webpackIsomorphicTools.assets()} component={<div/>} store={store}/>))
  } else {
    universalRouter(location, undefined, store, req.headers.host)
      .then(({component, transition, isRedirect}) => {
        if (isRedirect) {
          res.redirect(transition.redirectInfo.pathname)
          return
        }
        res.send('<!doctype html>\n' +
          ReactDOMServer.renderToString(<Html assets={webpackIsomorphicTools.assets()} component={component} store={store}/>))
      })
      .catch((error) => {
        if (error.redirect) {
          res.redirect(error.redirect)
          return
        }
        winston.error('ROUTER ERROR:', error)
        res.status(500).send({error: error.stack})
      })
  }
})

// express-winston errorLogger makes sense AFTER the router.
winston.add(NewrelicWinston, {})

app.use(expressWinston.logger({
  transports: [
    new winston.transports.Console({
      colorize: true
    })
  ],
  expressFormat: true,
  colorStatus: true
}))

if (config.port) {
  app.listen(config.port, (err) => {
    if (err) {
      winston.error(err)
    } else {
      authApi().then(() => {
        winston.info('==> ✅  Server is listening')
        winston.info('==> 🌎  %s running on port %s, API on port %s', config.app.name, config.port, config.apiPort)
        winston.info('----------\n==> 🚀  Open http://%s in a browser to view the app.', process.env.APP_DOMAIN)
      })
    }
  })
} else {
  winston.error('==>     ERROR: No PORT environment variable has been specified')
}
<commit_msg>disable frameguard from helmet middleware<commit_after>import raven from 'raven'
import newrelic from 'newrelic'
import Express from 'express'
import ReactDOMServer from 'react-dom/server'
import React from 'react'
import Location from 'react-router/lib/Location'
import config from './config'
import favicon from 'serve-favicon'
import compression from 'compression'
import helmet from 'helmet'
import path from 'path'
import createStore from './redux/create'
import authApi from './api/api'
import ApiClient from './ApiClient'
import universalRouter from './universalRouter'
import Html from './Html'
import responseTime from 'response-time'
import winston from 'winston'
import expressWinston from 'express-winston'
import NewrelicWinston from 'newrelic-winston'
import proxy from 'http-proxy-middleware'

const app = new Express()

app.use(responseTime())
app.use(compression())
app.use(helmet({
	frameguard: false
}))
app.use(favicon(path.join(__dirname, '..', 'static', 'favicon.ico')))
app.use(require('serve-static')(path.join(__dirname, '..', 'static')))

app.use('/auth', proxy({
  target: `http://localhost:${config.apiPort}`,
  pathRewrite: { '^/auth': '' }
}))

app.get(/\/users|\/pt\/users|\/membros/, function(req, res) {
  res.redirect('/')
})

app.get('/robots.txt', function(req, res) {
  res.send('User-Agent: * \n' +
    'Allow: /')
})

if ( (app.get('env') === 'production') || (app.get('env') === 'staging') ) {
  // The error handler must be before any other error middleware
  app.use(raven.middleware.express.errorHandler(process.env.SENTRY_DSN))
}

app.use((req, res, next) => {
  const host = req.headers.host
  const isAppDomain = host === process.env.APP_DOMAIN
  if (isAppDomain) {
    res.redirect(301, `${req.protocol}://app.${host}${req.originalUrl}`)
    return
  }
  next()
})

app.use((req, res, next) => {
  const host = req.headers.host
  const isAppSubdomain = host.indexOf(`app.${process.env.APP_DOMAIN}`) !== -1
  const www = host.match(/^www\.(.*)/)
  const domains = require('fs').readFileSync('./src/redirect.blacklist')
  const lines = domains.toString().split('\n')
  const blacklist = lines.some(line => { if (line) return host.match(line) })

  if (!isAppSubdomain && !__DISABLE_SSR__ && !www && !blacklist) {
    res.redirect(301, `${req.protocol}://www.${host}`)
    return
  }
  else if (!isAppSubdomain && !__DISABLE_SSR__ && www && blacklist) {
    res.redirect(301, `${req.protocol}://${www[1]}`)
    return
  }
  next()
})

app.use((req, res) => {
  if (__DEVELOPMENT__) {
    // Do not cache webpack stats: the script file would change since
    // hot module replacement is enabled in the development env
    webpackIsomorphicTools.refresh()
  }

  const client = new ApiClient(req)
  const store = createStore(client)
  const location = new Location(req.path, req.query)
  if (__DISABLE_SSR__) {
    res.send('<!doctype html>\n' +
      ReactDOMServer.renderToString(<Html assets={webpackIsomorphicTools.assets()} component={<div/>} store={store}/>))
  } else {
    universalRouter(location, undefined, store, req.headers.host)
      .then(({component, transition, isRedirect}) => {
        if (isRedirect) {
          res.redirect(transition.redirectInfo.pathname)
          return
        }
        res.send('<!doctype html>\n' +
          ReactDOMServer.renderToString(<Html assets={webpackIsomorphicTools.assets()} component={component} store={store}/>))
      })
      .catch((error) => {
        if (error.redirect) {
          res.redirect(error.redirect)
          return
        }
        winston.error('ROUTER ERROR:', error)
        res.status(500).send({error: error.stack})
      })
  }
})

// express-winston errorLogger makes sense AFTER the router.
winston.add(NewrelicWinston, {})

app.use(expressWinston.logger({
  transports: [
    new winston.transports.Console({
      colorize: true
    })
  ],
  expressFormat: true,
  colorStatus: true
}))

if (config.port) {
  app.listen(config.port, (err) => {
    if (err) {
      winston.error(err)
    } else {
      authApi().then(() => {
        winston.info('==> ✅  Server is listening')
        winston.info('==> 🌎  %s running on port %s, API on port %s', config.app.name, config.port, config.apiPort)
        winston.info('----------\n==> 🚀  Open http://%s in a browser to view the app.', process.env.APP_DOMAIN)
      })
    }
  })
} else {
  winston.error('==>     ERROR: No PORT environment variable has been specified')
}
<|endoftext|>


<commit_before>import {RTM_EVENTS, CLIENT_EVENTS} from '@slack/client';
import schedule from 'node-schedule';
import moment from 'moment-timezone';

import {slack, logger} from './resources';
import {startExpress} from './routes';
import {loadTodayOrders, restaurants} from './utils';
import {loadUsers, messageReceived, endOfOrders, makeLastCall} from './slack';

/**
 * Starts the bot server
 */
export function runServer() {
  startExpress();

  loadUsers();
  const rtm = slack.rtm;
  logger.log('Starting Slack RTM client');
  rtm.start();

  rtm.on(RTM_EVENTS.MESSAGE, messageReceived);

  rtm.on(CLIENT_EVENTS.RTM.RTM_CONNECTION_OPENED, () => {
    // the timeout is here to go around a bug where connection is opened, but not properly established
    logger.log('Slack RTM client connected');
    setTimeout(loadTodayOrders, 3000);
  });

  const isDST = moment().tz('Europe/Prague').isDST();

  // set up last calls for each restaurant
  schedule.scheduleJob(`15 ${isDST ? 7 : 8} * * 1-5`, () => {
    makeLastCall();
  });

  schedule.scheduleJob(`30 ${isDST ? 6 : 7} * * 1-5`, () => {
    loadUsers();
  });

  schedule.scheduleJob(`30 ${isDST ? 7 : 8} * * 1-5`, () => {
    endOfOrders(restaurants.hamka);
  });

  schedule.scheduleJob(`40 ${isDST ? 7 : 8} * * 1-5`, () => {
    endOfOrders(restaurants.veglife);
  });

  schedule.scheduleJob(`00 ${isDST ? 8 : 9} * * 1-5`, () => {
    endOfOrders(restaurants.click);
  });

  schedule.scheduleJob(`00 ${isDST ? 8 : 9} * * 1-5`, () => {
    endOfOrders(restaurants.presto);
  });
}
<commit_msg>handle change in daylight saving time<commit_after>import {RTM_EVENTS, CLIENT_EVENTS} from '@slack/client';
import schedule from 'node-schedule';
import moment from 'moment-timezone';

import {slack, logger} from './resources';
import {startExpress} from './routes';
import {loadTodayOrders, restaurants} from './utils';
import {loadUsers, messageReceived, endOfOrders, makeLastCall} from './slack';

let wasDST = null;
let jobs = [];

function reschedule() {
  const isDST = moment().tz('Europe/Prague').isDST();

  if (isDST === wasDST) {
    return;
  }

  wasDST = isDST;

  jobs.forEach((j) => j.cancel);

  jobs = [
    // set up last calls for each restaurant
    schedule.scheduleJob(`15 ${isDST ? 7 : 8} * * 1-5`, () => {
      makeLastCall();
    }),
    schedule.scheduleJob(`30 ${isDST ? 6 : 7} * * 1-5`, () => {
      loadUsers();
    }),
    schedule.scheduleJob(`30 ${isDST ? 7 : 8} * * 1-5`, () => {
      endOfOrders(restaurants.hamka);
    }),
    schedule.scheduleJob(`40 ${isDST ? 7 : 8} * * 1-5`, () => {
      endOfOrders(restaurants.veglife);
    }),
    schedule.scheduleJob(`00 ${isDST ? 8 : 9} * * 1-5`, () => {
      endOfOrders(restaurants.click);
    }),
    schedule.scheduleJob(`00 ${isDST ? 8 : 9} * * 1-5`, () => {
      endOfOrders(restaurants.presto);
    }),
  ];
}

/**
 * Starts the bot server
 */
export function runServer() {
  startExpress();

  loadUsers();
  const rtm = slack.rtm;
  logger.log('Starting Slack RTM client');
  rtm.start();

  rtm.on(RTM_EVENTS.MESSAGE, messageReceived);

  rtm.on(CLIENT_EVENTS.RTM.RTM_CONNECTION_OPENED, () => {
    // the timeout is here to go around a bug where connection is opened, but not properly established
    logger.log('Slack RTM client connected');
    setTimeout(loadTodayOrders, 3000);
  });

  reschedule();

  setInterval(reschedule, 60 * 60 * 1000);
}
<|endoftext|>


<commit_before>var exec = require('child_process').exec;
var chalk = require('chalk');

// Output styling.
var success = chalk.green;
var notice = chalk.yellow;
var whoops = chalk.bold.red; // Can't name error as exec uses error in callback.
var underline = chalk.underline;

exports.init = function() {
	exec('git init', function(error, stdout, stderr) {
		if (stdout.substring(0, 13) === 'Reinitialized') {
			console.log(notice('[~] Reinitialized existing Git repository.'));
		} else {
			console.log(success('[✔] Initialized new Git repository.'));
		}
	});
};

exports.clone = function(repository) {
	// Nothing to clone.
	if (!repository) {
		console.log(notice('[~] You must enter a repository URL.'));
	} else {
		exec('git clone ' + repository, function(error, stdout, stderr) {
			if (stderr.substring(0, 7) === 'Cloning') {
				if (stderr.split('\n')[1].split(' ')[0] === 'fatal:') {
					console.log(whoops('[✖] Could not clone repository.'));
				} else {
					console.log(success('[✔] Cloned into ' + stderr.split('\n')[0].split(' ')[2]));
				}
			} else {
				if (stderr.substring(0, 23) === 'fatal: destination path') {
					console.log(notice('[~] Directory ' + stderr.split(' ')[3] + ' is not empty.'));
				} else {
					console.log(whoops('[✖] Could not clone repository.'));
				}
			}
		});
	}
};

exports.addAll = function() {
	exec('git add -A');

	console.log(success('[✔] Added everything!'));
};

exports.addSingle = function(target) {
	//TODO: Fix the Error handling! Currently just ignores warnings
	exec('git add ' + target, function(error, stdout, stderr) {
		if (stderr.substring(0, 7) !== 'warning') {
			console.log(whoops('[✖] Could not add target specified. Are you sure the target exists?'));
		} else {
			if (typeof target === 'undefined') { // Then just add all.
				exports.addAll();
			} else {
				console.log(success('[✔] Added ' + target + '!'));
			}
		}
	});
};

exports.addOptions = function(options, target) {
	// TODO: Fix the Error Handling! Currently just ignores warnings
	exec('git add -' + options + ' ' + target, function(error, stdout, stderr) {
		if (stderr.substring(0, 7) !== 'warning') {
			console.log(whoops('[✖] Could not add target specified. Are you sure the target exists?'));
		} else {
			if (typeof target === 'undefined') { // Then just add all.
				exports.addAll();
			} else {
				console.log(success('[✔] Added ' + target + '!'));
			}
		}
	});
};

exports.commit = function(message) {
	// No commit message.
	if (message === '') {
		console.log(notice('[~] You must enter a commit message.'));
	} else {
		exports.addAll();

		exec('git commit -m "' + message + '"', function(error, stdout, stderr) {
			if (stdout.substring(0, 1) === '[') {
				console.log(success('[✔] Commited!'));

				console.log();
				console.log('"' + underline(message) + '"');
			} else if (stdout.substring(0, 2) === 'On') {
				console.log(notice('[~] Everything already committed.'));
			} else {
				console.log(whoops('[✖] Could not commit changes.') + chalk.red(' Try again if there was a large amount of changes to add.'));
			}
		});
	}
};

exports.push = function() {
	exec('git push', function(error, stdout, stderr) {
		if (stderr.substring(0, 10) === 'Everything') {
			console.log(notice('[~] All commits already pushed.'));
		} else if (stderr.substring(0, 2) === 'To') {
			console.log(success('[✔] Pushed!'));
		} else {
			// TODO: add handling for non-fast-forwards (when you need to pull first, merge, and then push).
			console.log(whoops('[✖] Could not push commits.') + chalk.red(' (may have to pull first, merge any conflicts, and then attempt to push again using the ') + chalk.red.underline('standard git command') + chalk.red('.)'));
		}
	});
};

exports.pull = function() {
	exec('git pull', function(error, stdout, stderr) {
		if (stdout.substring(0, 7) === 'Already') {
			console.log(notice('[~] Everything is already up-to-date.'));
		} else if (stdout.substring(0, 8) === 'Updating') {
			console.log(success('[✔] Pulled!'));
		} else {
			// TODO: add handling for merge conflicts.
			console.log(whoops('[✖] Could not pull commits.') + chalk.red(' (may have to pull using the ') + chalk.red.underline('standard git command') + chalk.red(' to handle merge conflicts.)'));
		}
	});
};

exports.fetch = function() {
	exec('git fetch', function(error, stdout, stderr) {
		if (stdout === '' && stderr === '') {
			console.log(success('[✔] Fetched!'));
		} else {
			console.log(whoops('[✖] Could not fetch commits.'));
		}
	});
};

exports.status = function() {
	exec('git status', function(error, stdout, stderr) {
		var status = stdout.split('\n');

		// Branch.
		console.log(chalk.dim('branch  ') + chalk.gray.dim(' | ') + success(status[0].split(' ')[2]));

		// Position in branch.
		var position;
		switch (status[1].split(' ')[3]) {
			case 'behind':
				position = 'position' + chalk.gray.dim(' | ') + notice(status[1].split(' ')[7] + ' commits behind (fetch or pull)');
				break;

			case 'working': // Quick hack to get it working with freshly initialized git repositories.
			case 'up-to-date':
				position = chalk.dim('position') + chalk.gray.dim(' | ') + success('up-to-date');
				break;

			case 'ahead':
				position = 'position' + chalk.gray.dim(' | ') + notice(status[1].split(' ')[7] + ' commits ahead (push commits)');
				break;

			default:
				// TODO: add proper handling for freshly initialized git repositories.
				if (typeof status[1].split(' ')[3] === 'undefined') { // Quick hack to get it working with freshly initialized git repositories.
					position = chalk.dim('position') + chalk.gray.dim(' | ') + success('up-to-date');
				} else if (status[1] === 'Changes to be committed:') {
					position = chalk.dim('position') + chalk.gray.dim(' | ') + success('up-to-date');
				} else {
					position = 'position' + chalk.gray.dim(' | ') + whoops('diverged');
				}
		}
		console.log(position);

		console.log();

		// Staging and commits.
		var staging;
		if (status[3] === 'Changes not staged for commit:') {
			staging = 'staging ' + chalk.gray.dim(' | ') + notice('not all changes staged (add all changes)');
		} else {
			if (status[1] !== 'nothing to commit, working directory clean') {
				if (status[4].split(' ')[0] === 'Untracked') {
					staging = 'staging ' + chalk.gray.dim(' | ') + notice('not all changes staged (add all changes)');
				} else {
					staging = chalk.dim('staging ') + chalk.gray.dim(' | ') + success('all changes staged');
				}
			}
		}

		console.log(staging);

		var commits;
		if (status[3] === 'Changes to be committed:') {
			commits = 'commits ' + chalk.gray.dim(' | ') + notice('not all changes committed (commit all changes)');
		} else if (status[1] === 'Changes to be committed:') {
			commits = 'commits ' + chalk.gray.dim(' | ') + notice('not all changes committed (commit all changes)');
		} else {
			commits = chalk.dim('commits ') + chalk.gray.dim(' | ') + success('all changes committed');
		}

		console.log(commits);
	});
};

// https://gist.github.com/textarcana/1306223
exports.log = function() {
	exec('git log --pretty=format:\'{%n  "commit": "%H",%n  "author": "%an <%ae>",%n  "date": "%ad",%n  "message": "%f"%n},\' $@ | perl -pe \'BEGIN{print "["}; END{print "]\n"}\' | perl -pe \'s/},]/}]/\'', function(error, stdout, stderr) {
		var log = JSON.parse(stdout);
		log.reverse(); // Show newest at the bottom.

		for (var i = 0; i < log.length; i++) {
			console.log(); // New line between each commit.

			console.log(chalk.yellow('[' + log[i].commit + ']'));

			var author = {
				name: log[i].author.split('<')[0].trim(),
				email: '<' + log[i].author.split('<')[1]
			};

			console.log('|- ' + chalk.gray(author.name) + " " + chalk.gray.dim(author.email));
			console.log('|- ' + chalk.gray.dim(log[i].date));
			console.log('|- ' + chalk.green(log[i].message.split('-').join(' ')));
		}
	});
};
<commit_msg>Add errorhandling for staging.<commit_after>var exec = require('child_process').exec;
var chalk = require('chalk');

// Output styling.
var success = chalk.green;
var notice = chalk.yellow;
var whoops = chalk.bold.red; // Can't name error as exec uses error in callback.
var underline = chalk.underline;

exports.init = function() {
	exec('git init', function(error, stdout, stderr) {
		if (stdout.substring(0, 13) === 'Reinitialized') {
			console.log(notice('[~] Reinitialized existing Git repository.'));
		} else {
			console.log(success('[✔] Initialized new Git repository.'));
		}
	});
};

exports.clone = function(repository) {
	// Nothing to clone.
	if (!repository) {
		console.log(notice('[~] You must enter a repository URL.'));
	} else {
		exec('git clone ' + repository, function(error, stdout, stderr) {
			if (stderr.substring(0, 7) === 'Cloning') {
				if (stderr.split('\n')[1].split(' ')[0] === 'fatal:') {
					console.log(whoops('[✖] Could not clone repository.'));
				} else {
					console.log(success('[✔] Cloned into ' + stderr.split('\n')[0].split(' ')[2]));
				}
			} else {
				if (stderr.substring(0, 23) === 'fatal: destination path') {
					console.log(notice('[~] Directory ' + stderr.split(' ')[3] + ' is not empty.'));
				} else {
					console.log(whoops('[✖] Could not clone repository.'));
				}
			}
		});
	}
};

exports.addAll = function() {
	exec('git add -A');

	console.log(success('[✔] Added everything!'));
};

exports.addSingle = function(target) {
	//TODO: Fix the Error handling! Currently just ignores warnings
	exec('git add ' + target, function(error, stdout, stderr) {
		if (stderr.substring(0, 7) !== 'warning') {
			console.log(whoops('[✖] Could not add target specified. Are you sure the target exists?'));
		} else {
			if (typeof target === 'undefined') { // Then just add all.
				exports.addAll();
			} else {
				console.log(success('[✔] Added ' + target + '!'));
			}
		}
	});
};

exports.addOptions = function(options, target) {
	// TODO: Fix the Error Handling! Currently just ignores warnings
	exec('git add -' + options + ' ' + target, function(error, stdout, stderr) {
		if (stderr.substring(0, 7) !== 'warning') {
			console.log(whoops('[✖] Could not add target specified. Are you sure the target exists?'));
		} else {
			if (typeof target === 'undefined') { // Then just add all.
				exports.addAll();
			} else {
				console.log(success('[✔] Added ' + target + '!'));
			}
		}
	});
};

exports.commit = function(message) {
	// No commit message.
	if (message === '') {
		console.log(notice('[~] You must enter a commit message.'));
	} else {
		exports.addAll();

		exec('git commit -m "' + message + '"', function(error, stdout, stderr) {
			if (stdout.substring(0, 1) === '[') {
				console.log(success('[✔] Commited!'));

				console.log();
				console.log('"' + underline(message) + '"');
			} else if (stdout.substring(0, 2) === 'On') {
				console.log(notice('[~] Everything already committed.'));
			} else {
				console.log(whoops('[✖] Could not commit changes.') + chalk.red(' Try again if there was a large amount of changes to add.'));
			}
		});
	}
};

exports.push = function() {
	exec('git push', function(error, stdout, stderr) {
		if (stderr.substring(0, 10) === 'Everything') {
			console.log(notice('[~] All commits already pushed.'));
		} else if (stderr.substring(0, 2) === 'To') {
			console.log(success('[✔] Pushed!'));
		} else {
			// TODO: add handling for non-fast-forwards (when you need to pull first, merge, and then push).
			console.log(whoops('[✖] Could not push commits.') + chalk.red(' (may have to pull first, merge any conflicts, and then attempt to push again using the ') + chalk.red.underline('standard git command') + chalk.red('.)'));
		}
	});
};

exports.pull = function() {
	exec('git pull', function(error, stdout, stderr) {
		if (stdout.substring(0, 7) === 'Already') {
			console.log(notice('[~] Everything is already up-to-date.'));
		} else if (stdout.substring(0, 8) === 'Updating') {
			console.log(success('[✔] Pulled!'));
		} else {
			// TODO: add handling for merge conflicts.
			console.log(whoops('[✖] Could not pull commits.') + chalk.red(' (may have to pull using the ') + chalk.red.underline('standard git command') + chalk.red(' to handle merge conflicts.)'));
		}
	});
};

exports.fetch = function() {
	exec('git fetch', function(error, stdout, stderr) {
		if (stdout === '' && stderr === '') {
			console.log(success('[✔] Fetched!'));
		} else {
			console.log(whoops('[✖] Could not fetch commits.'));
		}
	});
};

exports.status = function() {
	exec('git status', function(error, stdout, stderr) {
		var status = stdout.split('\n');

		// Branch.
		console.log(chalk.dim('branch  ') + chalk.gray.dim(' | ') + success(status[0].split(' ')[2]));

		// Position in branch.
		var position;
		switch (status[1].split(' ')[3]) {
			case 'behind':
				position = 'position' + chalk.gray.dim(' | ') + notice(status[1].split(' ')[7] + ' commits behind (fetch or pull)');
				break;

			case 'working': // Quick hack to get it working with freshly initialized git repositories.
			case 'up-to-date':
				position = chalk.dim('position') + chalk.gray.dim(' | ') + success('up-to-date');
				break;

			case 'ahead':
				position = 'position' + chalk.gray.dim(' | ') + notice(status[1].split(' ')[7] + ' commits ahead (push commits)');
				break;

			default:
				// TODO: add proper handling for freshly initialized git repositories.
				if (typeof status[1].split(' ')[3] === 'undefined') { // Quick hack to get it working with freshly initialized git repositories.
					position = chalk.dim('position') + chalk.gray.dim(' | ') + success('up-to-date');
				} else if (status[1] === 'Changes to be committed:') {
					position = chalk.dim('position') + chalk.gray.dim(' | ') + success('up-to-date');
				} else {
					position = 'position' + chalk.gray.dim(' | ') + whoops('diverged');
				}
		}
		console.log(position);

		console.log();

		// Staging and commits.
		var staging;
		if (status[3] === 'Changes not staged for commit:') {
			staging = 'staging ' + chalk.gray.dim(' | ') + notice('not all changes staged (add all changes)');
		} else {
			if (status[1] !== 'nothing to commit, working directory clean') {
				if (status[4].split(' ')[0] === 'Untracked') {
					staging = 'staging ' + chalk.gray.dim(' | ') + notice('not all changes staged (add all changes)');
				} else {
					staging = chalk.dim('staging ') + chalk.gray.dim(' | ') + success('all changes staged');
				}
			} else {
				staging = chalk.dim('staging ') + chalk.gray.dim(' | ') + success('all changes staged');
			}
		}

		console.log(staging);

		var commits;
		if (status[3] === 'Changes to be committed:') {
			commits = 'commits ' + chalk.gray.dim(' | ') + notice('not all changes committed (commit all changes)');
		} else if (status[1] === 'Changes to be committed:') {
			commits = 'commits ' + chalk.gray.dim(' | ') + notice('not all changes committed (commit all changes)');
		} else {
			commits = chalk.dim('commits ') + chalk.gray.dim(' | ') + success('all changes committed');
		}

		console.log(commits);
	});
};

// https://gist.github.com/textarcana/1306223
exports.log = function() {
	exec('git log --pretty=format:\'{%n  "commit": "%H",%n  "author": "%an <%ae>",%n  "date": "%ad",%n  "message": "%f"%n},\' $@ | perl -pe \'BEGIN{print "["}; END{print "]\n"}\' | perl -pe \'s/},]/}]/\'', function(error, stdout, stderr) {
		var log = JSON.parse(stdout);
		log.reverse(); // Show newest at the bottom.

		for (var i = 0; i < log.length; i++) {
			console.log(); // New line between each commit.

			console.log(chalk.yellow('[' + log[i].commit + ']'));

			var author = {
				name: log[i].author.split('<')[0].trim(),
				email: '<' + log[i].author.split('<')[1]
			};

			console.log('|- ' + chalk.gray(author.name) + " " + chalk.gray.dim(author.email));
			console.log('|- ' + chalk.gray.dim(log[i].date));
			console.log('|- ' + chalk.green(log[i].message.split('-').join(' ')));
		}
	});
};
<|endoftext|>


<commit_before>import Promise from "./promise";
import all from "./all";
import { isArray, isFunction } from "./utils";

/**

 `RSVP.map` is similar to JavaScript's native `map` method, except that it
  waits for all promises to become fulfilled before running the `mapFn` on
  each item in given to `promises`. `RSVP.map` returns a promise that will
  become fulfilled with the result of running `mapFn` on the values the promises
  become fulfilled with.

  For example:

  ```javascript

  var promise1 = RSVP.resolve(1);
  var promise2 = RSVP.resolve(2);
  var promise3 = RSVP.resolve(3);

  var mapFn = function(item){
    return item + 1;
  };

  RSVP.map(promises, mapFn).then(function(result){
    // result is [ 2, 3, 4 ]
  });
  ```

  If any of the `promises` given to `RSVP.map` are rejected, the first promise
  that is rejected will be given as an argument to the returned promises's
  rejection handler. For example:

  ```javascript
  var promise1 = RSVP.resolve(1);
  var promise2 = RSVP.reject(new Error("2"));
  var promise3 = RSVP.reject(new Error("3"));
  var promises = [ promise1, promise2, promise3 ];

  var mapFn = function(item){
    return item + 1;
  };

  RSVP.map(promises, mapFn).then(function(array){
    // Code here never runs because there are rejected promises!
  }, function(reason) {
    // reason.message === "2"
  });
  ```

  `RSVP.map` will also wait if a promise is returned from `mapFn`. For example,
  say you want to get all comments from a set of blog posts, but you need
  the blog posts first becuase they contain a url to those comments.

  ```javscript

  var mapFn = function(blogPost){
    // getComments does some ajax and returns an RSVP.Promise that is fulfilled
    // with some comments data
    return getComments(blogPost.comments_url);
  };

  // getBlogPosts does some ajax and returns an RSVP.Promise that is fulfilled
  // with some blog post data
  RSVP.map(getBlogPosts(), mapFn).then(function(comments){
    // comments is the result of asking the server for the comments
    // of all blog posts returned from getBlogPosts()
  });
  ```

  @method map
  @for RSVP
  @param {Array} promises
  @param {Function} mapFn function to be called on each fulfilled promise.
  @param {String} label optional string for labelling the promise.
  Useful for tooling.
  @return {Promise} promise that is fulfilled with the result of calling
  `mapFn` on each fulfilled promise or value when they become fulfilled.
   The promise will be rejected if any of the given `promises` become rejected.
*/
export default function map(promises, mapFn, label) {

  if (!isArray(promises)) {
    throw new TypeError('You must pass an array to map.');
  }

  if (!isFunction(mapFn)){
    throw new TypeError("You must pass a function to map's second argument.");
  }

  return all(promises, label).then(function(results){
    var resultLen = results.length,
        mappedResults = [],
        i;

    for (i = 0; i < resultLen; i++){
      mappedResults.push(mapFn(results[i]));
    }

    return all(mappedResults, label);
  });
};
<commit_msg>Minor adjustment to map inline doc<commit_after>import Promise from "./promise";
import all from "./all";
import { isArray, isFunction } from "./utils";

/**

 `RSVP.map` is similar to JavaScript's native `map` method, except that it
  waits for all promises to become fulfilled before running the `mapFn` on
  each item in given to `promises`. `RSVP.map` returns a promise that will
  become fulfilled with the result of running `mapFn` on the values the promises
  become fulfilled with.

  For example:

  ```javascript

  var promise1 = RSVP.resolve(1);
  var promise2 = RSVP.resolve(2);
  var promise3 = RSVP.resolve(3);
  var promises = [ promise1, promise2, promise3 ];

  var mapFn = function(item){
    return item + 1;
  };

  RSVP.map(promises, mapFn).then(function(result){
    // result is [ 2, 3, 4 ]
  });
  ```

  If any of the `promises` given to `RSVP.map` are rejected, the first promise
  that is rejected will be given as an argument to the returned promises's
  rejection handler. For example:

  ```javascript
  var promise1 = RSVP.resolve(1);
  var promise2 = RSVP.reject(new Error("2"));
  var promise3 = RSVP.reject(new Error("3"));
  var promises = [ promise1, promise2, promise3 ];

  var mapFn = function(item){
    return item + 1;
  };

  RSVP.map(promises, mapFn).then(function(array){
    // Code here never runs because there are rejected promises!
  }, function(reason) {
    // reason.message === "2"
  });
  ```

  `RSVP.map` will also wait if a promise is returned from `mapFn`. For example,
  say you want to get all comments from a set of blog posts, but you need
  the blog posts first becuase they contain a url to those comments.

  ```javscript

  var mapFn = function(blogPost){
    // getComments does some ajax and returns an RSVP.Promise that is fulfilled
    // with some comments data
    return getComments(blogPost.comments_url);
  };

  // getBlogPosts does some ajax and returns an RSVP.Promise that is fulfilled
  // with some blog post data
  RSVP.map(getBlogPosts(), mapFn).then(function(comments){
    // comments is the result of asking the server for the comments
    // of all blog posts returned from getBlogPosts()
  });
  ```

  @method map
  @for RSVP
  @param {Array} promises
  @param {Function} mapFn function to be called on each fulfilled promise.
  @param {String} label optional string for labelling the promise.
  Useful for tooling.
  @return {Promise} promise that is fulfilled with the result of calling
  `mapFn` on each fulfilled promise or value when they become fulfilled.
   The promise will be rejected if any of the given `promises` become rejected.
*/
export default function map(promises, mapFn, label) {

  if (!isArray(promises)) {
    throw new TypeError('You must pass an array to map.');
  }

  if (!isFunction(mapFn)){
    throw new TypeError("You must pass a function to map's second argument.");
  }

  return all(promises, label).then(function(results){
    var resultLen = results.length,
        mappedResults = [],
        i;

    for (i = 0; i < resultLen; i++){
      mappedResults.push(mapFn(results[i]));
    }

    return all(mappedResults, label);
  });
};
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var EventEmitter = require('events').EventEmitter
  , debug = require('debug')('mocha:runnable')
  , milliseconds = require('./ms');

/**
 * Save timer references to avoid Sinon interfering (see GH-237).
 */

var Date = global.Date
  , setTimeout = global.setTimeout
  , setInterval = global.setInterval
  , clearTimeout = global.clearTimeout
  , clearInterval = global.clearInterval;

/**
 * Object#toString().
 */

var toString = Object.prototype.toString;

/**
 * Expose `Runnable`.
 */

module.exports = Runnable;

/**
 * Initialize a new `Runnable` with the given `title` and callback `fn`.
 *
 * @param {String} title
 * @param {Function} fn
 * @api private
 */

function Runnable(title, fn) {
  this.title = title;
  this.fn = fn;
  this.async = fn && fn.length;
  this.sync = ! this.async;
  this._timeout = 2000;
  this._slow = 75;
  this.timedOut = false;
}

/**
 * Inherit from `EventEmitter.prototype`.
 */

Runnable.prototype.__proto__ = EventEmitter.prototype;

/**
 * Set & get timeout `ms`.
 *
 * @param {Number|String} ms
 * @return {Runnable|Number} ms or self
 * @api private
 */

Runnable.prototype.timeout = function(ms){
  if (0 == arguments.length) return this._timeout;
  if ('string' == typeof ms) ms = milliseconds(ms);
  debug('timeout %d', ms);
  this._timeout = ms;
  if (this.timer) this.resetTimeout();
  return this;
};

/**
 * Set & get slow `ms`.
 *
 * @param {Number|String} ms
 * @return {Runnable|Number} ms or self
 * @api private
 */

Runnable.prototype.slow = function(ms){
  if (0 === arguments.length) return this._slow;
  if ('string' == typeof ms) ms = milliseconds(ms);
  debug('timeout %d', ms);
  this._slow = ms;
  return this;
};

/**
 * Return the full title generated by recursively
 * concatenating the parent's full title.
 *
 * @return {String}
 * @api public
 */

Runnable.prototype.fullTitle = function(){
  return this.parent.fullTitle() + ' ' + this.title;
};

/**
 * Clear the timeout.
 *
 * @api private
 */

Runnable.prototype.clearTimeout = function(){
  clearTimeout(this.timer);
};

/**
 * Inspect the runnable void of private properties.
 *
 * @return {String}
 * @api private
 */

Runnable.prototype.inspect = function(){
  return JSON.stringify(this, function(key, val){
    if ('_' == key[0]) return;
    if ('parent' == key) return '#<Suite>';
    if ('ctx' == key) return '#<Context>';
    return val;
  }, 2);
};

/**
 * Reset the timeout.
 *
 * @api private
 */

Runnable.prototype.resetTimeout = function(){
  var self = this;
  var ms = this.timeout() || 1e9;

  this.clearTimeout();
  this.timer = setTimeout(function(){
    self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
    self.timedOut = true;
  }, ms);
};

/**
 * Whitelist these globals for this test run
 *
 * @api private
 */
Runnable.prototype.globals = function(arr){
  var self = this;
  this._allowedGlobals = arr;
};

/**
 * Run the test and invoke `fn(err)`.
 *
 * @param {Function} fn
 * @api private
 */

Runnable.prototype.run = function(fn){
  var self = this
    , ms = this.timeout()
    , start = new Date
    , ctx = this.ctx
    , finished
    , emitted;

  if (ctx) ctx.runnable(this);

  // timeout
  if (this.async) {
    if (ms) {
      this.timer = setTimeout(function(){
        done(new Error('timeout of ' + ms + 'ms exceeded'));
        self.timedOut = true;
      }, ms);
    }
  }

  // called multiple times
  function multiple(err) {
    if (emitted) return;
    emitted = true;
    self.emit('error', err || new Error('done() called multiple times'));
  }

  // finished
  function done(err) {
    if (self.timedOut) return;
    if (finished) return multiple(err);
    self.clearTimeout();
    self.duration = new Date - start;
    finished = true;
    fn(err);
  }

  // for .resetTimeout()
  this.callback = done;

  // async
  if (this.async) {
    try {
      this.fn.call(ctx, function(err){
        if (err instanceof Error || toString.call(err) === "[object Error]") return done(err);
        if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
        done();
      });
    } catch (err) {
      done(err);
    }
    return;
  }

  if (this.asyncOnly) {
    return done(new Error('--async-only option in use without declaring `done()`'));
  }

  // sync
  try {
    if (!this.pending) this.fn.call(ctx);
    this.duration = new Date - start;
    fn();
  } catch (err) {
    fn(err);
  }
};
<commit_msg>Add generator function support to Runnable.run()<commit_after>
/**
 * Module dependencies.
 */

var EventEmitter = require('events').EventEmitter
  , debug = require('debug')('mocha:runnable')
  , milliseconds = require('./ms'),
  , co = require('co');

/**
 * Save timer references to avoid Sinon interfering (see GH-237).
 */

var Date = global.Date
  , setTimeout = global.setTimeout
  , setInterval = global.setInterval
  , clearTimeout = global.clearTimeout
  , clearInterval = global.clearInterval;

/**
 * Object#toString().
 */

var toString = Object.prototype.toString;

/**
 * Expose `Runnable`.
 */

module.exports = Runnable;

/**
 * Initialize a new `Runnable` with the given `title` and callback `fn`.
 *
 * @param {String} title
 * @param {Function} fn
 * @api private
 */

function Runnable(title, fn) {
  this.title = title;
  this.fn = fn;
  this.async = fn && fn.length;
  this.sync = ! this.async;
  this._timeout = 2000;
  this._slow = 75;
  this.timedOut = false;
}

/**
 * Inherit from `EventEmitter.prototype`.
 */

Runnable.prototype.__proto__ = EventEmitter.prototype;

/**
 * Set & get timeout `ms`.
 *
 * @param {Number|String} ms
 * @return {Runnable|Number} ms or self
 * @api private
 */

Runnable.prototype.timeout = function(ms){
  if (0 == arguments.length) return this._timeout;
  if ('string' == typeof ms) ms = milliseconds(ms);
  debug('timeout %d', ms);
  this._timeout = ms;
  if (this.timer) this.resetTimeout();
  return this;
};

/**
 * Set & get slow `ms`.
 *
 * @param {Number|String} ms
 * @return {Runnable|Number} ms or self
 * @api private
 */

Runnable.prototype.slow = function(ms){
  if (0 === arguments.length) return this._slow;
  if ('string' == typeof ms) ms = milliseconds(ms);
  debug('timeout %d', ms);
  this._slow = ms;
  return this;
};

/**
 * Return the full title generated by recursively
 * concatenating the parent's full title.
 *
 * @return {String}
 * @api public
 */

Runnable.prototype.fullTitle = function(){
  return this.parent.fullTitle() + ' ' + this.title;
};

/**
 * Clear the timeout.
 *
 * @api private
 */

Runnable.prototype.clearTimeout = function(){
  clearTimeout(this.timer);
};

/**
 * Inspect the runnable void of private properties.
 *
 * @return {String}
 * @api private
 */

Runnable.prototype.inspect = function(){
  return JSON.stringify(this, function(key, val){
    if ('_' == key[0]) return;
    if ('parent' == key) return '#<Suite>';
    if ('ctx' == key) return '#<Context>';
    return val;
  }, 2);
};

/**
 * Reset the timeout.
 *
 * @api private
 */

Runnable.prototype.resetTimeout = function(){
  var self = this;
  var ms = this.timeout() || 1e9;

  this.clearTimeout();
  this.timer = setTimeout(function(){
    self.callback(new Error('timeout of ' + ms + 'ms exceeded'));
    self.timedOut = true;
  }, ms);
};

/**
 * Whitelist these globals for this test run
 *
 * @api private
 */
Runnable.prototype.globals = function(arr){
  var self = this;
  this._allowedGlobals = arr;
};

/**
 * Run the test and invoke `fn(err)`.
 *
 * @param {Function} fn
 * @api private
 */

Runnable.prototype.run = function(fn){
  var self = this
    , ms = this.timeout()
    , start = new Date
    , ctx = this.ctx
    , finished
    , emitted;

  if (ctx) ctx.runnable(this);

  // timeout
  if (this.async) {
    if (ms) {
      this.timer = setTimeout(function(){
        done(new Error('timeout of ' + ms + 'ms exceeded'));
        self.timedOut = true;
      }, ms);
    }
  }

  // called multiple times
  function multiple(err) {
    if (emitted) return;
    emitted = true;
    self.emit('error', err || new Error('done() called multiple times'));
  }

  // finished
  function done(err) {
    if (self.timedOut) return;
    if (finished) return multiple(err);
    self.clearTimeout();
    self.duration = new Date - start;
    finished = true;
    fn(err);
  }

  // for .resetTimeout()
  this.callback = done;

  // async
  if (this.async) {
    try {
      this.fn.call(ctx, function(err){
        if (err instanceof Error || toString.call(err) === "[object Error]") return done(err);
        if (null != err) return done(new Error('done() invoked with non-Error: ' + err));
        done();
      });
    } catch (err) {
      done(err);
    }
    return;
  }

  if (this.asyncOnly) {
    return done(new Error('--async-only option in use without declaring `done()`'));
  }

  var result;

  try {
    if (!this.pending) {
      result = this.fn.call(ctx);
    }

    // This is where we determine if the result is a generator
    if (result && typeof(result.next) == 'function' && typeof(result.throw) == 'function') {
      // Mocha timeout for async function
      if (ms) {
        this.timer = setTimeout(function(){
          done(new Error('timeout of ' + ms + 'ms exceeded'));
          self.timedOut = true;
        }, ms);
      }
      // Use co to run generator to completion
      co(result)(function(err) {
        this.duration = new Date - start;
        done(err);
      });
    } else {
      // Default Mocha handling of sync function
      this.duration = new Date - start;
      fn();
    }
  } catch (err) {
    fn(err);
  }
};
<|endoftext|>


<commit_before>const path = require('path');
const fs = require('fs-extra');

function exportIG(fhirResults, outDir) {
  // Copy the static parts of the IG
  fs.copySync(`${__dirname}/ig_files`, outDir);

  const igControlPath = path.join(outDir, 'shr.json');
  const igControl = fs.readJsonSync(igControlPath);
  const xmlResources = [];
  const htmlProfiles = [];
  const htmlExtensions = [];

  // For each profile and extension:
  // 1. Copy it into resources/StructureDefinition
  // 2. Add it to the IG JSON controle file
  // 3. Add it to the IG XML file
  // 4. Add it to the profiles or extensions HTML file
  // 5. Create the mapping markdown file (profiles only)
  const sdPath = path.join(outDir, 'resources', 'StructureDefinition');
  for (const profile of fhirResults.profiles.sort(sdCompare)) {
    fs.writeFileSync(path.join(sdPath, `${profile.id}.json`), JSON.stringify(profile, null, 2));
    igControl.resources[`StructureDefinition/${profile.id}`] = {
      'base': `StructureDefinition-${profile.id}.html`
    };
    xmlResources.push(`<resource>
            <example value="false" />
            <sourceReference>
                <reference value="StructureDefinition/${profile.id}" />
                <display value="${profile.name}" />
            </sourceReference>
        </resource>
        `);
    const namespace = getNamespaceFromID(profile.id);
    const name = profile.name.replace(/^SHR /, '').replace(/ Profile$/, '');
    htmlProfiles.push(`

          ${namespace}
          ${name}
          ${escapeHTML(profile.description)}

        `);

    var mapRegExp = /^.*
([^]*)<\/pre>.*$/mg;
    var match = mapRegExp.exec(profile.text.div);
    const mapPath = path.join(outDir, 'pages', '_includes', `${profile.id}-shr-mapping.xhtml`);
    if (match && match.length > 0) {
      fs.writeFileSync(mapPath,
`

SHR Mapping Source

This structure represents the following SHR mapping definition:

${match[1]}

`
      );
    } else {
      fs.writeFileSync(mapPath, '');
    }
  }
  for (const extension of fhirResults.extensions.sort(sdCompare)) {
    fs.writeFileSync(path.join(sdPath, `${extension.id}.json`), JSON.stringify(extension, null, 2));
    igControl.resources[`StructureDefinition/${extension.id}`] = {
      'base': `StructureDefinition-${extension.id}.html`
    };
    xmlResources.push(`<resource>
            <example value="false" />
            <sourceReference>
                <reference value="StructureDefinition/${extension.id}" />
                <display value="${extension.name}" />
            </sourceReference>
        </resource>
        `);
    const namespace = getNamespaceFromID(extension.id, true);
    const name = extension.name.replace(/^SHR /, '').replace(/ Extension$/, '');
    htmlExtensions.push(`

          ${namespace}
          ${name}
          ${escapeHTML(extension.description)}

        `);

    // Mappings don't really apply to extensions, but we have to create a file to keep Jekyll happy
    const mapPath = path.join(outDir, 'pages', '_includes', `${extension.id}-shr-mapping.xhtml`);
    fs.writeFileSync(mapPath, '');
  }

  // Rewrite the updated IG JSON control file
  fs.writeFileSync(igControlPath, JSON.stringify(igControl, null, 2));

  // Rewrite the updated IG XML file
  const igXmlPath = path.join(outDir, 'resources', 'ImplementationGuide', 'shr.xml');
  const igXml = fs.readFileSync(igXmlPath, 'utf8');
  fs.writeFileSync(igXmlPath, igXml.replace('<resources-go-here/>', xmlResources.join('')));

  // Rewrite the updated Profiles HTML file
  const profilesHtmlPath = path.join(outDir, 'pages', 'profiles.html');
  const profilesHtml = fs.readFileSync(profilesHtmlPath, 'utf8');
  fs.writeFileSync(profilesHtmlPath, profilesHtml.replace('<resources-go-here/>', htmlProfiles.join('')));

  // Rewrite the updated Extensions HTML file
  const extensionsHtmlPath = path.join(outDir, 'pages', 'extensions.html');
  const extensionsHtml = fs.readFileSync(extensionsHtmlPath, 'utf8');
  fs.writeFileSync(extensionsHtmlPath, extensionsHtml.replace('<extensions-go-here/>', htmlExtensions.join('')));
}

function sdCompare(a, b) {
  const [aID, bID] = [a.id.toLowerCase(), b.id.toLowerCase()];
  if (aID < bID) {
    return -1;
  } else if (aID > bID) {
    return 1;
  }
  return 0;
}

function getNamespaceFromID(id, isExtension=false) {
  const parts = id.split('-');
  const endIndex = isExtension ? parts.length-2 : parts.length-1;
  const nsParts = parts.slice(1, endIndex).map(p => p.charAt(0).toUpperCase() + p.slice(1));
  return nsParts.join(' ');
}

function escapeHTML(unsafe = '') {
  return unsafe
    .replace(/&/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"')
    .replace(/'/g, ''');
}

module.exports = {exportIG};<commit_msg>Write IG files using UTF8 encoding<commit_after>const path = require('path');
const fs = require('fs-extra');

function exportIG(fhirResults, outDir) {
  // Copy the static parts of the IG
  fs.copySync(`${__dirname}/ig_files`, outDir);

  const igControlPath = path.join(outDir, 'shr.json');
  const igControl = fs.readJsonSync(igControlPath);
  const xmlResources = [];
  const htmlProfiles = [];
  const htmlExtensions = [];

  // For each profile and extension:
  // 1. Copy it into resources/StructureDefinition
  // 2. Add it to the IG JSON controle file
  // 3. Add it to the IG XML file
  // 4. Add it to the profiles or extensions HTML file
  // 5. Create the mapping markdown file (profiles only)
  const sdPath = path.join(outDir, 'resources', 'StructureDefinition');
  for (const profile of fhirResults.profiles.sort(sdCompare)) {
    fs.writeFileSync(path.join(sdPath, `${profile.id}.json`), JSON.stringify(profile, null, 2), 'utf8');
    igControl.resources[`StructureDefinition/${profile.id}`] = {
      'base': `StructureDefinition-${profile.id}.html`
    };
    xmlResources.push(`<resource>
            <example value="false" />
            <sourceReference>
                <reference value="StructureDefinition/${profile.id}" />
                <display value="${profile.name}" />
            </sourceReference>
        </resource>
        `);
    const namespace = getNamespaceFromID(profile.id);
    const name = profile.name.replace(/^SHR /, '').replace(/ Profile$/, '');
    htmlProfiles.push(`

          ${namespace}
          ${name}
          ${escapeHTML(profile.description)}

        `);

    var mapRegExp = /^.*
([^]*)<\/pre>.*$/mg;
    var match = mapRegExp.exec(profile.text.div);
    const mapPath = path.join(outDir, 'pages', '_includes', `${profile.id}-shr-mapping.xhtml`);
    if (match && match.length > 0) {
      fs.writeFileSync(mapPath,
`

SHR Mapping Source

This structure represents the following SHR mapping definition:

${match[1]}

`
      , 'utf8');
    } else {
      fs.writeFileSync(mapPath, '', 'utf8');
    }
  }
  for (const extension of fhirResults.extensions.sort(sdCompare)) {
    fs.writeFileSync(path.join(sdPath, `${extension.id}.json`), JSON.stringify(extension, null, 2), 'utf8');
    igControl.resources[`StructureDefinition/${extension.id}`] = {
      'base': `StructureDefinition-${extension.id}.html`
    };
    xmlResources.push(`<resource>
            <example value="false" />
            <sourceReference>
                <reference value="StructureDefinition/${extension.id}" />
                <display value="${extension.name}" />
            </sourceReference>
        </resource>
        `);
    const namespace = getNamespaceFromID(extension.id, true);
    const name = extension.name.replace(/^SHR /, '').replace(/ Extension$/, '');
    htmlExtensions.push(`

          ${namespace}
          ${name}
          ${escapeHTML(extension.description)}

        `);

    // Mappings don't really apply to extensions, but we have to create a file to keep Jekyll happy
    const mapPath = path.join(outDir, 'pages', '_includes', `${extension.id}-shr-mapping.xhtml`);
    fs.writeFileSync(mapPath, '', 'utf8');
  }

  // Rewrite the updated IG JSON control file
  fs.writeFileSync(igControlPath, JSON.stringify(igControl, null, 2), 'utf8');

  // Rewrite the updated IG XML file
  const igXmlPath = path.join(outDir, 'resources', 'ImplementationGuide', 'shr.xml');
  const igXml = fs.readFileSync(igXmlPath, 'utf8');
  fs.writeFileSync(igXmlPath, igXml.replace('<resources-go-here/>', xmlResources.join('')), 'utf8');

  // Rewrite the updated Profiles HTML file
  const profilesHtmlPath = path.join(outDir, 'pages', 'profiles.html');
  const profilesHtml = fs.readFileSync(profilesHtmlPath, 'utf8');
  fs.writeFileSync(profilesHtmlPath, profilesHtml.replace('<resources-go-here/>', htmlProfiles.join('')), 'utf8');

  // Rewrite the updated Extensions HTML file
  const extensionsHtmlPath = path.join(outDir, 'pages', 'extensions.html');
  const extensionsHtml = fs.readFileSync(extensionsHtmlPath, 'utf8');
  fs.writeFileSync(extensionsHtmlPath, extensionsHtml.replace('<extensions-go-here/>', htmlExtensions.join('')), 'utf8');
}

function sdCompare(a, b) {
  const [aID, bID] = [a.id.toLowerCase(), b.id.toLowerCase()];
  if (aID < bID) {
    return -1;
  } else if (aID > bID) {
    return 1;
  }
  return 0;
}

function getNamespaceFromID(id, isExtension=false) {
  const parts = id.split('-');
  const endIndex = isExtension ? parts.length-2 : parts.length-1;
  const nsParts = parts.slice(1, endIndex).map(p => p.charAt(0).toUpperCase() + p.slice(1));
  return nsParts.join(' ');
}

function escapeHTML(unsafe = '') {
  return unsafe
    .replace(/&/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"')
    .replace(/'/g, ''');
}

module.exports = {exportIG};<|endoftext|>