Skip to content
This repository was archived by the owner on Sep 12, 2019. It is now read-only.

inject env vars everywhere #88

Merged
merged 3 commits into from
Apr 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,11 +132,11 @@ Function templates can specify `addons` that they rely on as well as execute arb
```js
// .netlify-function-template.js
module.exports = {
addons: ['fauna'],
addons: ["fauna"],
onComplete() {
console.log(`custom-template function created from template!`)
console.log(`custom-template function created from template!`);
}
}
};
```

#### Executing Netlify Functions
Expand Down
6 changes: 3 additions & 3 deletions src/commands/dev/exec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ const { track } = require("@netlify/cli-utils/src/utils/telemetry");

class ExecCommand extends Command {
async run() {
const { site } = this.netlify;
const { site, api } = this.netlify;
if (site.id) {
const accessToken = await this.authenticate();
const { addEnvVarsFromAddons } = require("../../utils/dev-exec");
await addEnvVarsFromAddons(site, accessToken);
const { addEnvVariables } = require("../../utils/dev");
await addEnvVariables(api, site, accessToken);
}
execa(this.argv[0], this.argv.slice(1), {
env: process.env,
Expand Down
107 changes: 47 additions & 60 deletions src/commands/dev/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const Command = require("@netlify/cli-utils");
const { getAddons } = require("netlify/src/addons");
const { track } = require("@netlify/cli-utils/src/utils/telemetry");
const chalk = require("chalk");
const NETLIFYDEV = `[${chalk.cyan("Netlify Dev")}]`;
const boxen = require("boxen");
const { createTunnel, connectTunnel } = require("../../live-tunnel");

Expand All @@ -24,58 +25,62 @@ function addonUrl(addonUrls, req) {
}

// Used as an optimization to avoid dual lookups for missing assets
const assetExtensionRegExp = /\.(html?|png|jpg|js|css|svg|gif|ico|woff|woff2)$/
const assetExtensionRegExp = /\.(html?|png|jpg|js|css|svg|gif|ico|woff|woff2)$/;

function alternativePathsFor(url) {
const paths = []
if (url[url.length - 1] === '/') {
const end = url.length - 1
if (url !== '/') {
paths.push(url.slice(0, end) + '.html')
paths.push(url.slice(0, end) + '.htm')
const paths = [];
if (url[url.length - 1] === "/") {
const end = url.length - 1;
if (url !== "/") {
paths.push(url.slice(0, end) + ".html");
paths.push(url.slice(0, end) + ".htm");
}
paths.push(url + 'index.html')
paths.push(url + 'index.htm')
paths.push(url + "index.html");
paths.push(url + "index.htm");
} else if (!url.match(assetExtensionRegExp)) {
paths.push(url + '.html')
paths.push(url + '.htm')
paths.push(url + '/index.html')
paths.push(url + '/index.htm')
paths.push(url + ".html");
paths.push(url + ".htm");
paths.push(url + "/index.html");
paths.push(url + "/index.htm");
}

return paths
return paths;
}

function initializeProxy(port) {
const proxy = httpProxy.createProxyServer({
selfHandleResponse: true,
target: {
host: 'localhost',
host: "localhost",
port: port
}
})
});

proxy.on('proxyRes', (proxyRes, req, res) => {
if (proxyRes.statusCode === 404 && req.alternativePaths && req.alternativePaths.length) {
req.url = req.alternativePaths.shift()
return proxy.web(req, res, req.proxyOptions)
proxy.on("proxyRes", (proxyRes, req, res) => {
if (
proxyRes.statusCode === 404 &&
req.alternativePaths &&
req.alternativePaths.length
) {
req.url = req.alternativePaths.shift();
return proxy.web(req, res, req.proxyOptions);
}
res.writeHead(proxyRes.statusCode, proxyRes.headers)
proxyRes.on('data', function(data) {
res.write(data)
})
proxyRes.on('end', function() {
res.end()
})
})
res.writeHead(proxyRes.statusCode, proxyRes.headers);
proxyRes.on("data", function(data) {
res.write(data);
});
proxyRes.on("end", function() {
res.end();
});
});

return {
web: (req, res, options) => {
req.proxyOptions = options
req.alternativePaths = alternativePathsFor(req.url)
return proxy.web(req, res, options)
req.proxyOptions = options;
req.alternativePaths = alternativePathsFor(req.url);
return proxy.web(req, res, options);
}
}
};
}

async function startProxy(settings, addonUrls) {
Expand Down Expand Up @@ -134,7 +139,7 @@ function startDevServer(settings, log, error) {
const StaticServer = require("static-server");
if (!settings.dist) {
log(
"Unable to determine public folder for the dev server.\nSetup a netlify.toml file with a [dev] section to specify your dev server settings."
`${NETLIFYDEV} Unable to determine public folder for the dev server. \n Setup a netlify.toml file with a [dev] section to specify your dev server settings.`
);
process.exit(1);
}
Expand All @@ -149,10 +154,10 @@ function startDevServer(settings, log, error) {
});

server.start(function() {
log("Server listening to", settings.proxyPort);
log(`${NETLIFYDEV} Server listening to`, settings.proxyPort);
});
} else {
log(`Starting netlify dev with ${settings.type}`);
log(`${NETLIFYDEV} Starting Netlify Dev with ${settings.type}`);
const ps = execa(settings.command, settings.args, {
env: settings.env,
stdio: "inherit"
Expand All @@ -171,39 +176,21 @@ class DevCommand extends Command {
flags.functions ||
(config.dev && config.dev.functions) ||
(config.build && config.build.functions);
const addonUrls = {};
let addonUrls = {};

let accessToken = api.accessToken;
if (site.id && !flags.offline) {
accessToken = await this.authenticate();
const addons = await getAddons(site.id, accessToken);
if (Array.isArray(addons)) {
addons.forEach(addon => {
addonUrls[addon.slug] = `${addon.config.site_url}/.netlify/${
addon.slug
}`;
for (const key in addon.env) {
process.env[key] = process.env[key] || addon.env[key];
}
});
}
const api = this.netlify.api;
const apiSite = await api.getSite({ site_id: site.id });
// TODO: We should move the environment outside of build settings and possibly have a
// `/api/v1/sites/:site_id/environment` endpoint for it that we can also gate access to
// In the future and that we could make context dependend
if (apiSite.build_settings && apiSite.build_settings.env) {
for (const key in apiSite.build_settings.env) {
process.env[key] =
process.env[key] || apiSite.build_settings.env[key];
}
}
const { addEnvVariables } = require("../../utils/dev");
addonUrls = await addEnvVariables(api, site, accessToken);
}
process.env.NETLIFY_DEV = "true";

let settings = serverSettings(config.dev);
if (!(settings && settings.command)) {
this.log("No dev server detected, using simple static server");
this.log(
"[Netlify Dev] No dev server detected, using simple static server"
);
const dist =
(config.dev && config.dev.publish) ||
(config.build && config.build.publish);
Expand Down Expand Up @@ -253,7 +240,7 @@ class DevCommand extends Command {
live: flags.live || false
});

const banner = chalk.bold(`Netlify dev server is now ready on ${url}`);
const banner = chalk.bold(`Netlify Dev Server now ready on ${url}`);
this.log(
boxen(banner, {
padding: 1,
Expand Down
6 changes: 2 additions & 4 deletions src/commands/functions/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,10 +395,8 @@ async function installAddons(addons = [], fnPath) {
if (addonCreateMsg) {
// spinner.success("installed addon: " + addonName);
if (addonDidInstall) {
const {
addEnvVarsFromAddons
} = require("../../utils/dev-exec");
await addEnvVarsFromAddons(site, accessToken);
const { addEnvVariables } = require("../../utils/dev");
await addEnvVariables(api, site, accessToken);
const { confirmPostInstall } = await inquirer.prompt([
{
type: "confirm",
Expand Down
5 changes: 4 additions & 1 deletion src/detect-server.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
const path = require("path");
const chalk = require("chalk");
const NETLIFYDEV = `[${chalk.cyan("Netlify Dev")}]`;

const detectors = require("fs")
.readdirSync(path.join(__dirname, "detectors"))
.filter(x => x.endsWith(".js")) // only accept .js detector files
Expand Down Expand Up @@ -39,7 +42,7 @@ function assignLoudly(settings, settingsField, newValue) {
if (settings[settingsField] !== newValue) {
// silent if command is exactly same
console.log(
`Overriding ${settingsField} with setting derived from netlify.toml [dev] block: `,
`${NETLIFYDEV} Overriding ${settingsField} with setting derived from netlify.toml [dev] block: `,
newValue
);
settings[settingsField] === newValue;
Expand Down
28 changes: 0 additions & 28 deletions src/utils/dev-exec.js

This file was deleted.

72 changes: 72 additions & 0 deletions src/utils/dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// reusable code for netlify dev
// bit of a hasty abstraction but recommended by oclif
const { getAddons } = require("netlify/src/addons");
const chalk = require("chalk");
const NETLIFYDEV = `[${chalk.cyan("Netlify Dev")}]`;
/**
* inject environment variables from netlify addons and buildbot
* into your local dev process.env
*
* ```
* // usage example
* const { site } = this.netlify
* if (site.id) {
* const accessToken = await this.authenticate()
* const addonUrls = await addEnvVariables(site, accessToken)
* // addonUrls is only for startProxy in netlify dev:index
* }
* ```
*/
async function addEnvVariables(api, site, accessToken) {
/** from addons */
const addonUrls = {};
const addons = await getAddons(site.id, accessToken);
if (Array.isArray(addons)) {
addons.forEach(addon => {
addonUrls[addon.slug] = `${addon.config.site_url}/.netlify/${addon.slug}`;
for (const key in addon.env) {
const msg = () =>
console.log(`${NETLIFYDEV} Injected addon env var: `, key);
process.env[key] = assignLoudly(process.env[key], addon.env[key], msg);
}
});
}

/** from web UI */
const apiSite = await api.getSite({ site_id: site.id });
// TODO: We should move the environment outside of build settings and possibly have a
// `/api/v1/sites/:site_id/environment` endpoint for it that we can also gate access to
// In the future and that we could make context dependend
if (apiSite.build_settings && apiSite.build_settings.env) {
for (const key in apiSite.build_settings.env) {
const msg = () =>
console.log(`${NETLIFYDEV} Injected build setting env var: `, key);
process.env[key] = assignLoudly(
process.env[key],
apiSite.build_settings.env[key],
msg
);
}
}

return addonUrls;
}

module.exports = {
addEnvVariables
};

// if first arg is undefined, use default, but tell user about it in case it is unintentional
function assignLoudly(
optionalValue,
defaultValue,
tellUser = dV => console.log(`No value specified, using fallback of `, dV)
) {
if (defaultValue === undefined) throw new Error("must have a defaultValue");
if (defaultValue !== optionalValue && optionalValue === undefined) {
tellUser(defaultValue);
return defaultValue;
} else {
return optionalValue;
}
}