Skip to content

Instantly share code, notes, and snippets.

@kroneman
Created January 31, 2019 17:13
Show Gist options
  • Save kroneman/47d379db60011ffb5b2a29ce9920cfc1 to your computer and use it in GitHub Desktop.
Save kroneman/47d379db60011ffb5b2a29ce9920cfc1 to your computer and use it in GitHub Desktop.
mkcert-localhost-certificate
const util = require('util');
const path = require('path');
const exec = util.promisify(require('child_process').exec);
/**
* Script for obtaining a certificate for local development using
* Inspiration https://github.com/Shopify/slate/issues/726#issuecomment-425462689
* @dependencies
* - OSx, this script won't work on windows
* - Brew, in order to install mkcert (manual process)
* https://www.howtogeek.com/211541/homebrew-for-os-x-easily-installs-desktop-apps-and-terminal-utilities/
* - $ brew install mkcert
* https://github.com/FiloSottile/mkcert
* - $ mkcert -install [installs local certificate authority]
* - then run this script
*/
/**
* Config to run any of the functions in the file directly
* @typedef executionConfig
* @param {RegularExpression} ipAddressTest
* @param {String} outputDirectory
* @param {String} [localIpAddress] - by default this is null and will need to be filled to run createCertificate
*/
const relativeOutputDir = './.certificates';
const config = {
// Test String for contents of ip address
// source: https://www.regular-expressions.info/ip.html
domain: 'localhost',
ipAddressTest: /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/,
outputFileMatch: /"\.\/(\S+?)"/gm,
outputDirectory: path.resolve(relativeOutputDir),
localIpAddress: null,
};
const bashCommands = {
getLocalIP: 'ipconfig getifaddr $(route get default | grep interface | awk \'{print $2}\')',
makeCertificate: ({dir, ipAddress}) => `cd ${dir} && mkcert localhost ${ipAddress} ::1`,
changeDirectory: (dir) => `cd ${dir}`,
createDirectory: (dir) => `mkdir ${dir}`,
removeDirectory: (dir) => `rm -r ${dir}`,
};
// eslint-disable-next-line no-undef
module.exports = main;
/**
* Main execution
*/
function main(executionConfig) {
return changeToOutputDirectory(executionConfig)
.then(getLocalIpAddress)
.then(createCertificate)
.then(matchOutputFiles)
.catch((err) => {
// eslint-disable-next-line no-console,no-undef
console.error(err);
});
}
/**
* mkcert places certificate into directory the process is run from,
* so pogramatically changing directories
* @param {executionConfig} executionConfig - see typedef
* @returns {Promise<Object>}
*/
async function changeToOutputDirectory(executionConfig) {
try {
// normal flow, try to remove directory, if fails it probably doesn't exist
// eslint-disable-next-line no-console,no-undef
console.log(`${executionConfig.outputDirectory}: removing directory...`);
await exec(bashCommands.removeDirectory(executionConfig.outputDirectory));
// eslint-disable-next-line no-console,no-undef
console.log(`${executionConfig.outputDirectory}: creating directory...`);
await exec(bashCommands.createDirectory(executionConfig.outputDirectory));
// eslint-disable-next-line no-console,no-undef
console.log(`${executionConfig.outputDirectory}: changing into directory...`);
await exec(bashCommands.changeDirectory(executionConfig.outputDirectory));
} catch (error) {
// eslint-disable-next-line no-console,no-undef
console.log(`${executionConfig.outputDirectory}: doesn't exist... creating it`);
await exec(bashCommands.createDirectory(executionConfig.outputDirectory));
}
return executionConfig;
}
/**
* @param {executionConfig} executionConfig - see typedef
* @returns {Promise<string>} empty string if error
*/
async function getLocalIpAddress(executionConfig) {
const localIpAddress = await exec(bashCommands.getLocalIP);
const {stdout, stderr} = localIpAddress;
if (stderr && stderr.length > 0) {
throw new Error(stderr, ' : is not a valid ip address');
}
if (!config.ipAddressTest.test(stdout)) {
throw new Error(stdout, ' : is not a valid ip address');
}
// remove newlines from stdout
return {
...executionConfig,
localIpAddress: stdout.replace('\n', ''),
};
}
/**
* @param {executionConfig} executionConfig - see typedef
* @returns {Promise<string>} empty string if error
*/
async function createCertificate(executionConfig) {
const makeCertConfig = {
dir: executionConfig.outputDirectory,
ipAddress: executionConfig.localIpAddress,
};
const createCertificateProcess = await exec(bashCommands.makeCertificate(makeCertConfig));
const {stderr} = createCertificateProcess;
// For some reason mkcert outputs success using stderr, if that changes re-enable these lines
// if (stderr && stderr.length > 0) {
// throw new Error(stderr);
// }
return {
...executionConfig,
result: stderr,
};
}
/**
* Updates the output with the exact location of the files
* @returns { key: String, cert: String} relative locations of the key and certificate to the project
*/
function matchOutputFiles(executionConfig) {
const filenames = executionConfig.result.match(executionConfig.outputFileMatch);
let newResult = executionConfig.result;
const returnValue = {};
let lenFileNames = filenames.length;
while (lenFileNames--) {
const currentFileName = filenames[lenFileNames];
let updatedFilePath = currentFileName.replace('./', '');
updatedFilePath = updatedFilePath.replace(/(")/g, '');
newResult = newResult.replace(currentFileName, `${executionConfig.outputDirectory}/${updatedFilePath} \n`);
// eslint-disable-next-line no-negated-condition
const isCertKey = updatedFilePath.indexOf('key') !== -1 ? 'key' : 'cert';
returnValue[isCertKey ? 'key' : 'cert'] = updatedFilePath;
}
// eslint-disable-next-line no-console,no-undef
console.log(newResult);
return returnValue;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment