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

Make netlify dev faster (reuse accessToken) #91

Merged
merged 4 commits into from
Apr 6, 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
32 changes: 27 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,49 @@ Netlify CLI plugin for local dev experience.

## What is Netlify Dev?

Netlify Dev brings the power of Netlify's Edge Logic layer, serverless functions and [add-on ecosystem](#using-add-ons) to your local laptop. It runs Netlify's production routing engine in a local dev server to make all redirects, proxy rules, function routes or add-on routes available locally and injects the correct environment variables from your site environment, installed add-ons or your netlify.toml file into your build and function environment.
Netlify Dev brings the power of Netlify's Edge Logic layer, [serverless functions](#netlify-functions) and [add-on ecosystem](#using-add-ons) to your local machine. It runs Netlify's production routing engine in a local dev server to make all redirects, proxy rules, function routes or add-on routes available locally and injects the correct environment variables from your site environment, installed add-ons or your netlify.toml file into your build and function environment.

It automatically detects common tools like Gatsby, Hugo, React Static, Eleventy, and more, to give a zero config setup for your local dev server and can help scaffolding new functions as you work on them.

## Prerequisites

There are just two:

- You should be [logged in on Netlify CLI](https://www.netlify.com/docs/cli/#authentication)
- Your project should be linked to a `siteID` on Netlify (using [netlify init](https://www.netlify.com/docs/cli/#continuous-deployment) or [netlify link](https://www.netlify.com/docs/cli/#linking-and-unlinking-sites)). You can confirm this has been done if you have a `.netlify` folder with a `state.json` file containing your `siteID`.

This is how we pull down your build environment variables and manage your addons on your local machine.

## Usage

- `netlify dev` start a local dev server for the build tool you're using
- `netlify dev:exec <command>` runs a shell command within the netlify dev environment
- `netlify functions:create` bootstrap a new function

As these commands are expected to be frequently used, it may be helpful to define aliases in your terminal (Mac: [bash](https://jonsuh.com/blog/bash-command-line-shortcuts/), [zsh](https://askubuntu.com/questions/758496/how-to-make-a-permanent-alias-in-oh-my-zsh), Windows: [doskey](https://stackoverflow.com/questions/20530996/aliases-in-windows-command-prompt), [registry](https://stackoverflow.com/questions/20530996/aliases-in-windows-command-prompt)) to your personal preference. For example:

```bash
## ~/.zshrc
alias ndeploy="netlify deploy --prod"
alias nd="netlify dev"
alias ndl="netlify dev --live"
alias nfc="netlify functions:create"
alias ndx="netlify dev:exec "
```

## Using the beta

Currently the Netlify dev plugin is in private beta. You'll need to follow these steps to enable it:

Make sure Netlify CLI is installed and up to date:

```
```bash
npm install -g netlify-cli
```

Then clone and activate the plugin:

```
```bash
git clone git@github.com:netlify/netlify-dev-plugin.git
cd netlify-dev-plugin
npm install
Expand Down Expand Up @@ -161,7 +181,7 @@ Add-ons are a way for Netlify users to extend the functionality of their Jamstac

To try out an add-on with Netlify dev, run the `netlify addons:create` command:

```
```bash
netlify addons:create fauna
```

Expand All @@ -175,8 +195,10 @@ After you have installed an add-on, it will be visible with the `netlify addons:

To share your ongoing dev session with a coworker, just run Netlify Dev with a `--live` flag:

```
```bash
netlify dev --live
```

You will get a URL that looks like `https://clever-cray-2aa156-6639f3.netlify.live/`. This can be accessed by anyone as long as you keep your session open.

> Note: there are currently known issues with ending the live session alongside your webdevserver. We are working on fixing it. In the mean time you can run `ps aux | grep live-tunnel` and kill these sessions manually.
5 changes: 4 additions & 1 deletion src/commands/dev/exec.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ class ExecCommand extends Command {
async run() {
const { site, api } = this.netlify;
if (site.id) {
const accessToken = await this.authenticate();
console.log(
`${NETLIFYDEV} Checking your site's environment variables...`
);
const accessToken = api.accessToken;
const { addEnvVariables } = require("../../utils/dev");
await addEnvVariables(api, site, accessToken);
} else {
Expand Down
6 changes: 3 additions & 3 deletions src/commands/dev/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,7 @@ function startDevServer(settings, log, error) {

class DevCommand extends Command {
async run() {
this.log(`${NETLIFYDEV} Starting Netlify Dev...`);
const { flags, args } = this.parse(DevCommand);
const { api, site, config } = this.netlify;
const functionsDir =
Expand All @@ -180,7 +181,6 @@ class DevCommand extends Command {

let accessToken = api.accessToken;
if (site.id && !flags.offline) {
accessToken = await this.authenticate();
const { addEnvVariables } = require("../../utils/dev");
addonUrls = await addEnvVariables(api, site, accessToken);
}
Expand All @@ -189,7 +189,7 @@ class DevCommand extends Command {
let settings = serverSettings(config.dev);
if (!(settings && settings.command)) {
this.log(
"[Netlify Dev] No dev server detected, using simple static server"
`${NETLIFYDEV} No dev server detected, using simple static server`
);
const dist =
(config.dev && config.dev.publish) ||
Expand Down Expand Up @@ -240,7 +240,7 @@ class DevCommand extends Command {
live: flags.live || false
});

const banner = chalk.bold(`Netlify Dev Server now ready on ${url}`);
const banner = chalk.bold(`${NETLIFYDEV} Server now ready on ${url}`);
this.log(
boxen(banner, {
padding: 1,
Expand Down
14 changes: 9 additions & 5 deletions src/commands/functions/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ const fs = require("fs");
const { flags } = require("@oclif/command");
const Command = require("@netlify/cli-utils");
const { zipFunctions } = require("@netlify/zip-it-and-ship-it");
const chalk = require("chalk");
const NETLIFYDEV = `[${chalk.cyan("Netlify Dev")}]`;

class FunctionsBuildCommand extends Command {
async run() {
Expand All @@ -12,27 +14,29 @@ class FunctionsBuildCommand extends Command {
const dst = flags.functions || config.build.functions;

if (src === dst) {
this.log("Source and destination for function build can't be the same");
this.log(
`${NETLIFYDEV} Source and destination for function build can't be the same`
);
process.exit(1);
}

if (!src || !dst) {
if (!src)
this.log(
"You must specify a source folder with a --src flag or a functionsSource field in your config"
`${NETLIFYDEV} Error: You must specify a source folder with a --src flag or a functionsSource field in your config`
);
if (!dst)
this.log(
"You must specify a destination functions folder with a --functions flag or a functions field in your config"
`${NETLIFYDEV} Error: You must specify a destination functions folder with a --functions flag or a functions field in your config`
);
process.exit(1);
}

fs.mkdirSync(dst, { recursive: true });

this.log("Building functions");
this.log(`${NETLIFYDEV} Building functions`);
zipFunctions(src, dst, { skipGo: true });
this.log("Functions built to ", dst);
this.log(`${NETLIFYDEV} Functions built to `, dst);
}
}

Expand Down
40 changes: 22 additions & 18 deletions src/commands/functions/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ const cp = require("child_process");
const { createAddon } = require("netlify/src/addons");
const ora = require("ora");
const { track } = require("@netlify/cli-utils/src/utils/telemetry");
const chalk = require("chalk");
const NETLIFYDEV = `[${chalk.cyan("Netlify Dev")}]`;

const templatesDir = path.resolve(__dirname, "../../functions-templates");

Expand Down Expand Up @@ -161,7 +163,6 @@ async function pickTemplate() {
const filteredTemplateNames = filteredTemplates.map(x =>
input ? x.string : x
);
// console.log({ filteredTemplateNames })
return registry
.filter(t => filteredTemplateNames.includes(t.name + t.description))
.map(t => {
Expand Down Expand Up @@ -203,15 +204,17 @@ function ensureFunctionDirExists(flags, config) {
const functionsDir =
flags.functions || (config.build && config.build.functions);
if (!functionsDir) {
this.log("No functions folder specified in netlify.toml or as an argument");
this.log(
`${NETLIFYDEV} No functions folder specified in netlify.toml or as an argument`
);
process.exit(1);
}
if (!fs.existsSync(functionsDir)) {
this.log(
`functions folder ${functionsDir} specified in netlify.toml but folder not found, creating it...`
`${NETLIFYDEV} functions folder ${functionsDir} specified in netlify.toml but folder not found, creating it...`
);
fs.mkdirSync(functionsDir);
this.log(`functions folder ${functionsDir} created`);
this.log(`${NETLIFYDEV} functions folder ${functionsDir} created`);
}
return functionsDir;
}
Expand All @@ -227,7 +230,7 @@ async function downloadFromURL(flags, args, functionsDir) {
fs.lstatSync(fnFolder + ".js").isFile()
) {
this.log(
`A single file version of the function ${name} already exists at ${fnFolder}.js`
`${NETLIFYDEV} Warning: A single file version of the function ${name} already exists at ${fnFolder}.js. Terminating without further action.`
);
process.exit(1);
}
Expand All @@ -254,9 +257,11 @@ async function downloadFromURL(flags, args, functionsDir) {
})
);

this.log(`installing dependencies for ${nameToUse}...`);
this.log(`${NETLIFYDEV} Installing dependencies for ${nameToUse}...`);
cp.exec("npm i", { cwd: path.join(functionsDir, nameToUse) }, () => {
this.log(`installing dependencies for ${nameToUse} complete `);
this.log(
`${NETLIFYDEV} Installing dependencies for ${nameToUse} complete `
);
});

// read, execute, and delete function template file if exists
Expand Down Expand Up @@ -296,16 +301,13 @@ async function scaffoldFromTemplate(flags, args, functionsDir) {
try {
await downloadFromURL.call(this, flags, args, functionsDir);
} catch (err) {
console.error("Error downloading from URL: " + flags.url);
console.error(`${NETLIFYDEV} Error downloading from URL: ` + flags.url);
console.error(err);
process.exit(1);
}
} else if (chosentemplate === "report") {
console.log(
"opening in browser: https://github.com/netlify/netlify-dev-plugin/issues/new"
);
require("../../utils/openBrowser.js")(
"https://github.com/netlify/netlify-dev-plugin/issues/new"
`${NETLIFYDEV} Open in browser: https://github.com/netlify/netlify-dev-plugin/issues/new`
);
} else {
const {
Expand All @@ -323,7 +325,7 @@ async function scaffoldFromTemplate(flags, args, functionsDir) {
}

const name = await getNameFromArgs(args, flags, templateName);
this.log(`Creating function ${name}`);
this.log(`${NETLIFYDEV} Creating function ${name}`);
const functionPath = ensureFunctionPathIsOk.call(
this,
functionsDir,
Expand All @@ -338,7 +340,7 @@ async function scaffoldFromTemplate(flags, args, functionsDir) {
copy(pathToTemplate, functionPath, vars, async (err, createdFiles) => {
if (err) throw err;
createdFiles.forEach(filePath => {
this.log(`Created ${filePath}`);
this.log(`${NETLIFYDEV} Created ${filePath}`);
require("fs").chmodSync(path.resolve(filePath), 0o777);
if (filePath.includes("package.json")) hasPackageJSON = true;
});
Expand Down Expand Up @@ -377,12 +379,12 @@ async function installAddons(addons = [], fnPath) {
);
return false;
}
console.log("checking Netlify APIs...");
console.log(`${NETLIFYDEV} checking Netlify APIs...`);

return api.getSite({ siteId }).then(async siteData => {
const accessToken = await this.authenticate();
const accessToken = api.accessToken;
const arr = addons.map(({ addonName, addonDidInstall }) => {
console.log("installing addon: " + addonName);
console.log(`${NETLIFYDEV} installing addon: ` + addonName);
// will prompt for configs if not supplied - we do not yet allow for addon configs supplied by `netlify functions:create` command and may never do so
return createSiteAddon(
accessToken,
Expand Down Expand Up @@ -423,7 +425,9 @@ async function installAddons(addons = [], fnPath) {
function ensureFunctionPathIsOk(functionsDir, flags, name) {
const functionPath = path.join(functionsDir, name);
if (fs.existsSync(functionPath)) {
this.log(`Function ${functionPath} already exists, cancelling...`);
this.log(
`${NETLIFYDEV} Function ${functionPath} already exists, cancelling...`
);
process.exit(1);
}
return functionPath;
Expand Down
4 changes: 3 additions & 1 deletion src/commands/functions/serve.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const { Command, flags } = require("@oclif/command");
const chalk = require("chalk");
const NETLIFYDEV = `[${chalk.cyan("Netlify Dev")}]`;

class FunctionsServeCommand extends Command {
async run() {
this.log(`serve a function`);
this.log(`${NETLIFYDEV} NOT IMPLEMENTED YET: serve a function`);
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/commands/functions/update.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
const { Command, flags } = require("@oclif/command");
const chalk = require("chalk");
const NETLIFYDEV = `[${chalk.cyan("Netlify Dev")}]`;

class FunctionsUpdateCommand extends Command {
async run() {
this.log(`update a function`);
this.log(`${NETLIFYDEV} NOT IMPLEMENTED YET: update a function`);
}
}

Expand Down
6 changes: 4 additions & 2 deletions src/live-tunnel.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ const os = require("os");
const path = require("path");
const execa = require("execa");
const { fetchLatest, updateAvailable } = require("gh-release-fetch");
const chalk = require("chalk");
const NETLIFYDEV = `[${chalk.cyan("Netlify Dev")}]`;

async function createTunnel(siteId, netlifyApiToken, log) {
await installTunnelClient(log);
Expand All @@ -14,7 +16,7 @@ async function createTunnel(siteId, netlifyApiToken, log) {
);
process.exit(1);
}
log("Creating Live Tunnel for " + siteId);
log(`${NETLIFYDEV} Creating Live Tunnel for ` + siteId);
const url = `https://api.netlify.com/api/v1/live_sessions?site_id=${siteId}`;

const response = await fetch(url, {
Expand Down Expand Up @@ -71,7 +73,7 @@ async function installTunnelClient(log) {
return;
}

log("Installing Live Tunnel Client");
log(`${NETLIFYDEV} Installing Live Tunnel Client`);

const win = isWindows();
const platform = win ? "windows" : process.platform;
Expand Down
4 changes: 2 additions & 2 deletions src/utils/dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ const NETLIFYDEV = `[${chalk.cyan("Netlify Dev")}]`;
*
* ```
* // usage example
* const { site } = this.netlify
* const { site, api } = this.netlify
* if (site.id) {
* const accessToken = await this.authenticate()
* const accessToken = api.accessToken
* const addonUrls = await addEnvVariables(site, accessToken)
* // addonUrls is only for startProxy in netlify dev:index
* }
Expand Down