From e9e2f8aa6438c4fd837c3bffbe1238ed42f1e8de Mon Sep 17 00:00:00 2001 From: sw-yx Date: Fri, 5 Apr 2019 01:15:07 -0400 Subject: [PATCH 1/3] inject env vars https://netlify.slack.com/archives/CGW2Y6XPH/p1554438987180700 --- src/commands/dev/exec.js | 6 +- src/commands/dev/index.js | 94 +++++++++++++------------------- src/commands/functions/create.js | 6 +- src/utils/dev-exec.js | 28 ---------- src/utils/dev.js | 46 ++++++++++++++++ 5 files changed, 90 insertions(+), 90 deletions(-) delete mode 100644 src/utils/dev-exec.js create mode 100644 src/utils/dev.js diff --git a/src/commands/dev/exec.js b/src/commands/dev/exec.js index 4788028..01ffe29 100644 --- a/src/commands/dev/exec.js +++ b/src/commands/dev/exec.js @@ -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, diff --git a/src/commands/dev/index.js b/src/commands/dev/index.js index 1ed589d..a78a6f5 100644 --- a/src/commands/dev/index.js +++ b/src/commands/dev/index.js @@ -24,58 +24,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) { @@ -171,33 +175,13 @@ 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"; diff --git a/src/commands/functions/create.js b/src/commands/functions/create.js index 2f01bce..e7846e7 100644 --- a/src/commands/functions/create.js +++ b/src/commands/functions/create.js @@ -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", diff --git a/src/utils/dev-exec.js b/src/utils/dev-exec.js deleted file mode 100644 index 7214159..0000000 --- a/src/utils/dev-exec.js +++ /dev/null @@ -1,28 +0,0 @@ -// code extracted from /dev/exec.js so we can reuse it in functions templating -// bit of a hasty abstraction but recommended by oclif -const { getAddons } = require("netlify/src/addons"); - -/** - * get this data from the `this.netlify` instance in your commands - * - * ``` - * // usage example - * const { site } = this.netlify - * if (site.id) { - * const accessToken = await this.authenticate() - * await addEnvVarsFromAddons(site, accessToken) - * } - * ``` - */ -async function addEnvVarsFromAddons(site, accessToken) { - const addons = await getAddons(site.id, accessToken); - addons.forEach(addon => { - for (const key in addon.env) { - process.env[key] = addon.env[key]; - } - }); -} - -module.exports = { - addEnvVarsFromAddons -}; diff --git a/src/utils/dev.js b/src/utils/dev.js new file mode 100644 index 0000000..d67fe2e --- /dev/null +++ b/src/utils/dev.js @@ -0,0 +1,46 @@ +// reusable code for netlify dev +// bit of a hasty abstraction but recommended by oclif +const { getAddons } = require("netlify/src/addons"); + +/** + * 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); + 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]; + } + }); + + /** 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) { + process.env[key] = process.env[key] || apiSite.build_settings.env[key]; + } + } + + return addonUrls; +} + +module.exports = { + addEnvVariables +}; From c6fd543b500cc49559b3e46d7ba69d2d51aec591 Mon Sep 17 00:00:00 2001 From: sw-yx Date: Fri, 5 Apr 2019 02:42:45 -0400 Subject: [PATCH 2/3] add netlify dev branding --- README.md | 6 +++--- src/commands/dev/index.js | 13 ++++++++----- src/detect-server.js | 5 ++++- src/utils/dev.js | 30 +++++++++++++++++++++++++++--- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 87e1a64..ee5fcc6 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/commands/dev/index.js b/src/commands/dev/index.js index a78a6f5..ccabcb3 100644 --- a/src/commands/dev/index.js +++ b/src/commands/dev/index.js @@ -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"); @@ -138,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); } @@ -153,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" @@ -187,7 +188,9 @@ class DevCommand extends Command { 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); @@ -237,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, diff --git a/src/detect-server.js b/src/detect-server.js index 36242e0..fe854fb 100644 --- a/src/detect-server.js +++ b/src/detect-server.js @@ -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 @@ -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; diff --git a/src/utils/dev.js b/src/utils/dev.js index d67fe2e..6db3191 100644 --- a/src/utils/dev.js +++ b/src/utils/dev.js @@ -1,7 +1,8 @@ // 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 @@ -23,7 +24,9 @@ async function addEnvVariables(api, site, accessToken) { 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 msg = () => + console.log(`${NETLIFYDEV} injected addon env var: `, key); + process.env[key] = assignLoudly(process.env[key], addon.env[key], msg); } }); @@ -34,7 +37,13 @@ async function addEnvVariables(api, site, accessToken) { // 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 msg = () => + console.log(`${NETLIFYDEV} injected build setting env var: `, key); + process.env[key] = assignLoudly( + process.env[key], + apiSite.build_settings.env[key], + msg + ); } } @@ -44,3 +53,18 @@ async function addEnvVariables(api, site, accessToken) { 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; + } +} From a2e896956140ac37694386e8e0d64864fe39a509 Mon Sep 17 00:00:00 2001 From: sw-yx Date: Fri, 5 Apr 2019 13:09:17 -0400 Subject: [PATCH 3/3] clean up presentation --- src/utils/dev.js | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/utils/dev.js b/src/utils/dev.js index 6db3191..830f4f0 100644 --- a/src/utils/dev.js +++ b/src/utils/dev.js @@ -21,14 +21,16 @@ async function addEnvVariables(api, site, accessToken) { /** from addons */ const addonUrls = {}; const addons = await getAddons(site.id, accessToken); - 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); - } - }); + 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 }); @@ -38,7 +40,7 @@ async function addEnvVariables(api, site, accessToken) { 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); + console.log(`${NETLIFYDEV} Injected build setting env var: `, key); process.env[key] = assignLoudly( process.env[key], apiSite.build_settings.env[key],