From 1033470c6971043c45f9cf4d383d573376116492 Mon Sep 17 00:00:00 2001 From: Wassim Chegham Date: Sun, 20 Oct 2019 19:11:09 +0200 Subject: [PATCH] feat: generate Bazel config for Functions --- README.md | 4 +- package.json | 2 +- src/core/utils.ts | 24 +++++++- src/features/functions/init.ts | 59 ++++++++++++++++++- src/features/hosting/index.ts | 6 +- src/index.ts | 4 ++ src/templates/init/functions/BUILD.bazel.tpl | 9 +++ .../init/functions/BUILD.root.bazel.tpl | 1 + src/templates/init/functions/WORKSPACE.tpl | 24 ++++++++ src/templates/init/functions/index.ts.tpl | 9 +++ .../init/{404.html => storage/404.html.tpl} | 0 .../{error.html => storage/error.html.tpl} | 0 .../{index.html => storage/index.html.tpl} | 0 13 files changed, 134 insertions(+), 8 deletions(-) create mode 100644 src/templates/init/functions/BUILD.bazel.tpl create mode 100644 src/templates/init/functions/BUILD.root.bazel.tpl create mode 100644 src/templates/init/functions/WORKSPACE.tpl create mode 100644 src/templates/init/functions/index.ts.tpl rename src/templates/init/{404.html => storage/404.html.tpl} (100%) rename src/templates/init/{error.html => storage/error.html.tpl} (100%) rename src/templates/init/{index.html => storage/index.html.tpl} (100%) diff --git a/README.md b/README.md index 307d1de..e4606d7 100644 --- a/README.md +++ b/README.md @@ -80,9 +80,9 @@ Options: login connect to your Azure init initialize a new workspace deploy deploy to Azure - -f, --force override all confirmations (default: false) + -y, --yes answer yes to all confirmations (default: false) -r, --relogin force login (default: false) - -c, --create enable resource creation (default: true) + -c, --create enable resource creation (default: false) -m, --manual enable Manual mode (default: false) -d, --debug enable debug mode (default: false) -s, --sas use SAS token (only: storage and database) (default: false) diff --git a/package.json b/package.json index 6a6fa2a..81b1df8 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "homepage": "https://github.com/manekinekko/hexa#readme", "private": false, "scripts": { - "start": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/index.ts", + "start": "npm run build -- --watch", "build": "rm -fr build/ && mkdir build && npm run copy && tsc", "copy": "cp -r src/templates build", "prepare": "npm run build", diff --git a/src/core/utils.ts b/src/core/utils.ts index 0810a81..e88b7e1 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -228,10 +228,18 @@ export function isProjectFileExists() { return isFound; } -export function copyTemplate(src: string, destination: string) { +export function copyTemplate(src: string, destination: string, context?: {[key: string]: string}) { const templateDir = getTemplateFullPath(); src = templateDir + "/" + src; + if (context) { + let srcContent = readFileFromDisk(src) || ""; + for (let key in context) { + srcContent = srcContent.replace(new RegExp(`{{(${key})}}`, 'g'), context[key]); + } + debug(`copying template file src=${chalk.green(src)}, destination=${chalk.green(destination)}, context=${chalk.green(JSON.stringify(context))}`); + return fs.writeFileSync(destination, srcContent); + } debug(`copying template file src=${chalk.green(src)}, destination=${chalk.green(destination)}`); return fs.copyFileSync(src, destination); } @@ -247,3 +255,17 @@ export function getFullPath(folder: string) { export function joinPath(...args: string[]) { return path.join(...args); } + +export function updateFile({filepath, replace, search}: {filepath: string, replace: string, search?: string}) { + let srcContent = readFileFromDisk(filepath) || ""; + + if (search) { + srcContent = srcContent.replace(search, replace); + } + else { + srcContent = [srcContent, replace].join(`\n`); + } + + debug(`updating file src=${chalk.green(filepath)}`); + return fs.writeFileSync(filepath, srcContent); +} diff --git a/src/features/functions/init.ts b/src/features/functions/init.ts index 97a90df..37b81c5 100644 --- a/src/features/functions/init.ts +++ b/src/features/functions/init.ts @@ -1,6 +1,6 @@ import chalk from "chalk"; import { askForFunctionsAppFolder } from "../../core/prompt"; -import { Config, createDirectoryIfNotExists, directoryExists, func, npm, saveWorkspace, uuid } from "../../core/utils"; +import { Config, createDirectoryIfNotExists, directoryExists, func, npm, saveWorkspace, uuid, copyTemplate, sanitize, updateFile } from "../../core/utils"; const debug = require("debug")("functions:init"); module.exports = async function() { @@ -54,7 +54,64 @@ module.exports = async function() { `${functionAppPath}`, `Scaffolding function ${chalk.cyan(functionHttpName)}...` ); + + // override the function index.ts with a simpler example + copyTemplate(`init/functions/index.ts.tpl`, `${functionAppPath}/${functionHttpName}/index.ts`, {functionHttpName: sanitize(functionHttpName)}); + await npm(`install`, functionAppPath, `Installing dependencies for ${chalk.cyan(functionAppPath)}...`); + // should be setup Bazel config? + if (process.env.HEXA_USE_BAZEL) { + await npm(`install -D @bazel/ibazel@latest @bazel/bazel@latest @bazel/typescript@latest`, functionAppPath, `Adding Bazel configuration for ${chalk.cyan(functionAppPath)}...`); + + // create the WORKSPACE + copyTemplate(`init/functions/WORKSPACE.tpl`, `${functionAppPath}/WORKSPACE`, {functionAppName: sanitize(functionAppName)}); + + // create the root BUILD.bazel file + copyTemplate(`init/functions/BUILD.root.bazel.tpl`, `${functionAppPath}/BUILD.bazel`); + + // create the BUILD.bazel file for the function + copyTemplate(`init/functions/BUILD.bazel.tpl`, `${functionAppPath}/${functionHttpName}/BUILD.bazel`, {functionHttpName: sanitize(functionHttpName)}); + + // update function.json to use Bazel's ${bazel-bin} file + updateFile({ + filepath: `${functionAppPath}/${functionHttpName}/function.json`, + replace: `../bazel-bin/${functionHttpName}/index.js`, + search: `../dist/${functionHttpName}/index.js`, + }); + + updateFile({ + filepath: `${functionAppPath}/${functionHttpName}/index.ts`, + replace: `../bazel-bin/${functionHttpName}/index.js`, + search: `.body: "Hello from Bazel"`, + }); + + updateFile({ + filepath: `${functionAppPath}/package.json`, + replace: `"build": "bazel build //..."`, + search: `"build": "tsc"`, + }); + + updateFile({ + filepath: `${functionAppPath}/package.json`, + replace: `"watch": "ibazel build //..."`, + search: `"watch": "tsc --w"`, + }); + + updateFile({ + filepath: `${functionAppPath}/.funcignore`, + replace: ` +## Bazel ignored files +node_modules/@bazel/* +BUILD.bazel + +# ignore all bazel output folder except the bazel-bin folder +# where the transpiled code lives +bazel-* +!bazel-bin + ` + }); + } + return true; }; diff --git a/src/features/hosting/index.ts b/src/features/hosting/index.ts index e58a634..aa0fd5c 100644 --- a/src/features/hosting/index.ts +++ b/src/features/hosting/index.ts @@ -22,15 +22,15 @@ module.exports = async function() { if (overrideHtml || typeof overrideHtml === "undefined") { // copy index.html - copyTemplate(`init/index.html`, `${folder}/index.html`); + copyTemplate(`init/hosting/index.html.tpl`, `${folder}/index.html`); } if (override404 || typeof override404 === "undefined") { // copy 404.html - copyTemplate(`init/404.html`, `${folder}/404.html`); + copyTemplate(`init/hosting/404.html.tpl`, `${folder}/404.html`); } if (overrideError || typeof overrideError === "undefined") { // copy errro.html - copyTemplate(`init/error.html`, `${folder}/error.html`); + copyTemplate(`init/hosting/error.html.tpl`, `${folder}/error.html`); } const storage: AzureStorage = Config.get("storage"); diff --git a/src/index.ts b/src/index.ts index be446b9..e766236 100644 --- a/src/index.ts +++ b/src/index.ts @@ -47,6 +47,7 @@ console.log(prettyFont.string); .option("-t, --token", "generate a Storage token into a .env file", false) .option("-j, --just ", "setup or deploy only the selected services (e.g. --just functions,hosting)", false) .option("--yolo", "enable all modes and all services", false) + .option("--use ", "use a specific build system (e.g. tsc,bazel)", 'tsc') .parse(process.argv); // set confiuration @@ -80,6 +81,9 @@ console.log(prettyFont.string); if (program.token) { process.env.HEXA_STORAGE_GENERATE_TOKEN = "1"; } + if (program.use === 'bazel') { + process.env.HEXA_USE_BAZEL = "1"; + } // use process.argv not program.argv const commandName = process.argv[2]; diff --git a/src/templates/init/functions/BUILD.bazel.tpl b/src/templates/init/functions/BUILD.bazel.tpl new file mode 100644 index 0000000..706da74 --- /dev/null +++ b/src/templates/init/functions/BUILD.bazel.tpl @@ -0,0 +1,9 @@ +load("@npm_bazel_typescript//:index.bzl", "ts_library") + +package(default_visibility = ["//visibility:public"]) + +ts_library( + name = "{{functionHttpName}}", + srcs = ["index.ts"], + deps = ["@npm//@azure/functions"] +) \ No newline at end of file diff --git a/src/templates/init/functions/BUILD.root.bazel.tpl b/src/templates/init/functions/BUILD.root.bazel.tpl new file mode 100644 index 0000000..d0cdd8b --- /dev/null +++ b/src/templates/init/functions/BUILD.root.bazel.tpl @@ -0,0 +1 @@ +exports_files(["tsconfig.json"], visibility = ["//:__subpackages__"]) \ No newline at end of file diff --git a/src/templates/init/functions/WORKSPACE.tpl b/src/templates/init/functions/WORKSPACE.tpl new file mode 100644 index 0000000..03642d1 --- /dev/null +++ b/src/templates/init/functions/WORKSPACE.tpl @@ -0,0 +1,24 @@ +workspace( + name = "{{functionAppName}}", + managed_directories = {"@npm": ["node_modules"]}, +) + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +http_archive( + name = "build_bazel_rules_nodejs", + sha256 = "1447312c8570e8916da0f5f415186e7098cdd4ce48e04b8e864f793c766959c3", + urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.38.2/rules_nodejs-0.38.2.tar.gz"], +) + +load("@build_bazel_rules_nodejs//:index.bzl", "npm_install") +npm_install( + name = "npm", + package_json = "//:package.json", + package_lock_json = "//:package-lock.json", +) + +load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") +install_bazel_dependencies() + +load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace") +ts_setup_workspace() diff --git a/src/templates/init/functions/index.ts.tpl b/src/templates/init/functions/index.ts.tpl new file mode 100644 index 0000000..865884b --- /dev/null +++ b/src/templates/init/functions/index.ts.tpl @@ -0,0 +1,9 @@ +import { AzureFunction, Context, HttpRequest } from "@azure/functions"; + +const httpTrigger: AzureFunction = async function (context: Context, req: HttpRequest): Promise { + context.res = { + body: "Hello {{functionHttpName}}" + }; +}; + +export default httpTrigger; diff --git a/src/templates/init/404.html b/src/templates/init/storage/404.html.tpl similarity index 100% rename from src/templates/init/404.html rename to src/templates/init/storage/404.html.tpl diff --git a/src/templates/init/error.html b/src/templates/init/storage/error.html.tpl similarity index 100% rename from src/templates/init/error.html rename to src/templates/init/storage/error.html.tpl diff --git a/src/templates/init/index.html b/src/templates/init/storage/index.html.tpl similarity index 100% rename from src/templates/init/index.html rename to src/templates/init/storage/index.html.tpl