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

Make addon install lifecycle #74

Merged
merged 2 commits into from
Apr 4, 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
12 changes: 3 additions & 9 deletions src/commands/dev/exec.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,20 @@
const execa = require('execa')
const Command = require('@netlify/cli-utils')
const { getAddons } = require('netlify/src/addons')

class ExecCommand extends Command {
async run() {
const { site } = this.netlify

if (site.id) {
const accessToken = await this.authenticate()
const addons = await getAddons(site.id, accessToken)
addons.forEach(addon => {
for (const key in addon.env) {
process.env[key] = addon.env[key]
}
})
const { addEnvVarsFromAddons } = require('../../utils/dev-exec')
await addEnvVarsFromAddons(site, accessToken)
}
execa(this.argv[0], this.argv.slice(1), { env: process.env, stdio: 'inherit' })
}
}

ExecCommand.description = `Exec command
Runs a command within the netlify dev environment
Runs a command within the netlify dev environment, e.g. with env variables from any installed addons
`

ExecCommand.examples = ['$ netlify exec npm run bootstrap']
Expand Down
19 changes: 13 additions & 6 deletions src/commands/functions/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,8 @@ async function downloadFromURL(flags, args, functionsDir) {
const fnTemplateFile = path.join(fnFolder, '.netlify-function-template.js')
if (fs.existsSync(fnTemplateFile)) {
const { onComplete, addons = [] } = require(fnTemplateFile)
installAddons.call(this, addons)

await installAddons.call(this, addons, path.resolve(fnFolder))
if (onComplete) onComplete()
fs.unlinkSync(fnTemplateFile) // delete
}
Expand Down Expand Up @@ -273,7 +274,7 @@ async function scaffoldFromTemplate(flags, args, functionsDir) {

const name = await getNameFromArgs(args, flags, templateName)
this.log(`Creating function ${name}`)
const functionPath = ensureFunctionPathIsOk(functionsDir, flags, name)
const functionPath = ensureFunctionPathIsOk.call(this, functionsDir, flags, name)

// // SWYX: note to future devs - useful for debugging source to output issues
// this.log('from ', pathToTemplate, ' to ', functionPath)
Expand All @@ -298,13 +299,13 @@ async function scaffoldFromTemplate(flags, args, functionsDir) {
this.log(`installing dependencies for ${name} complete `)
})
}
installAddons.call(this, addons)
installAddons.call(this, addons, path.resolve(functionPath))
if (onComplete) onComplete() // do whatever the template wants to do after it is scaffolded
})
}
}

async function installAddons(addons = []) {
async function installAddons(addons = [], fnPath) {
if (addons.length) {
const { api, site } = this.netlify
const siteId = site.id
Expand All @@ -314,10 +315,16 @@ async function installAddons(addons = []) {
}
return api.getSite({ siteId }).then(async siteData => {
const accessToken = await this.authenticate()
const arr = addons.map(addonName => {
const arr = addons.map(({ addonName, addonDidInstall }) => {
this.log('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, addonName, siteId, siteData, this.log)
return createSiteAddon(accessToken, addonName, siteId, siteData, this.log).then(async addonCreateMsg => {
if (addonCreateMsg && addonDidInstall) {
const { addEnvVarsFromAddons } = require('../../utils/dev-exec')
await addEnvVarsFromAddons(site, accessToken)
addonDidInstall(fnPath)
}
})
})
return Promise.all(arr)
})
Expand Down
23 changes: 23 additions & 0 deletions src/functions-templates/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,29 @@

place new templates here and our CLI will pick it up. each template must be in its own folder.

## not a long term solution

we dont want people to update their CLI every time we add a template. see https://github.com/netlify/netlify-dev-plugin/issues/42 for how we may solve in future

## template lifecycles

- onComplete
- meant for messages, logging, light cleanup
- onAllAddonsInstalled?
- not implemented yet
- meant for heavier work, but not sure if different from onComplete

## template addons

specify an array of objects of this shape:

```ts
{
addonName: String,
addonDidInstall?: Function // for executing arbitrary postinstall code for a SINGLE addon
}
```

## why place templates in a separate folder

we dont colocate this inside `src/commands/functions` because oclif will think it's a new command.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
const execa = require('execa')
module.exports = {
name: 'fauna-crud',
description: 'CRUD function using Fauna DB',
addons: ['fauna'], // in future we'll want to pass/prompt args to addons
addons: [
{
addonName: 'fauna',
addonDidInstall(fnPath) {
require('fs').chmodSync(fnPath + '/create-schema.js', 0o777)
execa.sync(fnPath + '/create-schema.js', undefined, { env: process.env, stdio: 'inherit' })
}
}
],
onComplete() {
console.log(`fauna-crud function created from template!`)
}
Expand Down
5 changes: 2 additions & 3 deletions src/utils/addons.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const inquirer = require('inquirer')
const fetch = require('node-fetch')

/** main section - shamelessly adapted from CLI. we can extract and dedupe later. */
/** i know, i know. i felt bad too. but we can DRY things up later. */
/** but we can DRY things up later. */
module.exports.createSiteAddon = async function createSiteAddon(accessToken, addonName, siteId, siteData, log) {
const addons = await getAddons(siteId, accessToken)
if (typeof addons === 'object' && addons.error) {
Expand All @@ -14,8 +14,6 @@ module.exports.createSiteAddon = async function createSiteAddon(accessToken, add
}
// Filter down addons to current args.name
const currentAddon = addons.find(addon => addon.service_path === `/.netlify/${addonName}`)
// // GET flags from `raw` data
// const rawFlags = parseRawFlags(raw)
const rawFlags = {}

if (currentAddon && currentAddon.id) {
Expand Down Expand Up @@ -109,6 +107,7 @@ module.exports.createSiteAddon = async function createSiteAddon(accessToken, add
accessToken,
siteData
})
return addonName // we dont really use this right now but may be helpful to know that an addon installation was successful
}

async function actuallyCreateSiteAddon({ addonName, settings, accessToken, siteData }) {
Expand Down
28 changes: 28 additions & 0 deletions src/utils/dev-exec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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
}