@faable/faable 1.4.12 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/deploy/check_environment.js +1 -0
- package/dist/commands/deploy/deploy_command.js +22 -32
- package/dist/commands/deploy/index.js +0 -5
- package/dist/commands/deploy/node-pipeline/analyze_package.js +34 -0
- package/dist/commands/deploy/node-pipeline/build_project.js +25 -0
- package/dist/{builder/node/prepare_dockerfile.js → commands/deploy/node-pipeline/bundle_docker.js} +12 -7
- package/dist/commands/deploy/node-pipeline/index.js +25 -0
- package/dist/commands/deploy/runtime-detect/runtime_detection.js +1 -2
- package/dist/commands/deploy/runtime-detect/strategies/docker.js +3 -1
- package/dist/commands/deploy/runtime-detect/strategies/nodejs.js +20 -2
- package/dist/index.js +11 -0
- package/dist/lib/Configuration.js +9 -8
- package/dist/log.js +9 -7
- package/package.json +2 -2
- package/dist/builder/docker/index.js +0 -10
- package/dist/builder/index.js +0 -17
- package/dist/builder/node/analyze_package.js +0 -25
- package/dist/builder/node/build_project.js +0 -27
- package/dist/builder/node/engine_version.js +0 -31
- package/dist/builder/node/index.js +0 -26
- package/dist/builder/python/index.js +0 -17
- package/dist/builder/python/prepare_dockerfile.js +0 -40
- package/dist/builder/python/templates/Dockerfile +0 -26
- package/dist/builder/python/templates/entrypoint.sh +0 -7
- package/dist/commands/deploy/runtime-detect/strategies/python.js +0 -19
- /package/dist/{builder/node → commands/deploy/node-pipeline}/templates/Dockerfile +0 -0
- /package/dist/{builder/node → commands/deploy/node-pipeline}/templates/entrypoint.sh +0 -0
|
@@ -2,19 +2,13 @@ import { log } from '../../log.js';
|
|
|
2
2
|
import { upload_tag } from './upload_tag.js';
|
|
3
3
|
import { check_environment } from './check_environment.js';
|
|
4
4
|
import { context } from '../../api/context.js';
|
|
5
|
+
import { build_node } from './node-pipeline/index.js';
|
|
5
6
|
import { runtime_detection } from './runtime-detect/runtime_detection.js';
|
|
6
|
-
import { import_builder } from '../../builder/index.js';
|
|
7
|
-
import { Configuration } from '../../lib/Configuration.js';
|
|
8
7
|
import { cmd } from '../../lib/cmd.js';
|
|
9
8
|
|
|
10
9
|
const deploy_command = async (args) => {
|
|
11
10
|
const workdir = args.workdir || process.cwd();
|
|
12
|
-
const flags = {
|
|
13
|
-
upload: args.onlybuild ? false : true,
|
|
14
|
-
};
|
|
15
11
|
const { api } = await context();
|
|
16
|
-
// Check if we can build docker images
|
|
17
|
-
await check_environment();
|
|
18
12
|
// Resolve runtime
|
|
19
13
|
const { app_name, runtime } = await runtime_detection(workdir);
|
|
20
14
|
const name = args.app_slug || app_name;
|
|
@@ -23,35 +17,31 @@ const deploy_command = async (args) => {
|
|
|
23
17
|
}
|
|
24
18
|
// Get app from Faable API
|
|
25
19
|
const app = await api.getBySlug(name);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// Upload to Faable registry
|
|
42
|
-
if (flags.upload) {
|
|
43
|
-
const { upload_tagname } = await upload_tag({ app, api });
|
|
44
|
-
// Create a deployment for this image
|
|
45
|
-
await api.createDeployment({
|
|
46
|
-
app_id: app.id,
|
|
47
|
-
image: upload_tagname,
|
|
48
|
-
...(params || {}),
|
|
20
|
+
// Check if we can build docker images
|
|
21
|
+
await check_environment();
|
|
22
|
+
log.info(`🚀 Deploying ${app.name} (${app.id}) runtime=${runtime.name}-${runtime.version} `);
|
|
23
|
+
let type;
|
|
24
|
+
if (runtime.name == "node") {
|
|
25
|
+
const node_result = await build_node(app, {
|
|
26
|
+
workdir,
|
|
27
|
+
runtime,
|
|
28
|
+
});
|
|
29
|
+
type = node_result.type;
|
|
30
|
+
}
|
|
31
|
+
else if (runtime.name == "docker") {
|
|
32
|
+
type = "node";
|
|
33
|
+
await cmd(`docker build -t ${app.id} .`, {
|
|
34
|
+
enableOutput: true,
|
|
49
35
|
});
|
|
50
|
-
log.info(`🌍 Deployment created -> https://${app.url}`);
|
|
51
36
|
}
|
|
52
37
|
else {
|
|
53
|
-
|
|
38
|
+
throw new Error(`No build pipeline for runtime=${runtime.name}`);
|
|
54
39
|
}
|
|
40
|
+
// Upload to Faable registry
|
|
41
|
+
const { upload_tagname } = await upload_tag({ app, api });
|
|
42
|
+
// Create a deployment for this image
|
|
43
|
+
await api.createDeployment({ app_id: app.id, image: upload_tagname, type });
|
|
44
|
+
log.info(`🌍 Deployment created -> https://${app.url}`);
|
|
55
45
|
};
|
|
56
46
|
|
|
57
47
|
export { deploy_command };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path__default from 'path';
|
|
3
|
+
import { log } from '../../../log.js';
|
|
4
|
+
import * as R from 'ramda';
|
|
5
|
+
|
|
6
|
+
const analyze_package = async (params) => {
|
|
7
|
+
const workdir = params.workdir;
|
|
8
|
+
const package_file = path__default.join(path__default.resolve(workdir), "package.json");
|
|
9
|
+
log.info(`Loading config from package.json`);
|
|
10
|
+
const pkg = await fs.readJSON(package_file);
|
|
11
|
+
// Check if build is required to run
|
|
12
|
+
const build_script = process.env.FAABLE_NPM_BUILD_SCRIPT
|
|
13
|
+
? process.env.FAABLE_NPM_BUILD_SCRIPT
|
|
14
|
+
: pkg?.scripts["build"]
|
|
15
|
+
? "build"
|
|
16
|
+
: null;
|
|
17
|
+
if (!build_script) {
|
|
18
|
+
log.info(`No build script on package.json`);
|
|
19
|
+
}
|
|
20
|
+
let type = "node";
|
|
21
|
+
// Detect nextjs deployment type
|
|
22
|
+
const next_dep = R.lensPath(["dependencies", "next"]);
|
|
23
|
+
const next_devdep = R.lensPath(["devDependencies", "next"]);
|
|
24
|
+
if (R.view(next_dep, pkg) || R.view(next_devdep, pkg)) {
|
|
25
|
+
type = "next";
|
|
26
|
+
}
|
|
27
|
+
log.info(`⚡️ Detected deployment type=${type}`);
|
|
28
|
+
return {
|
|
29
|
+
build_script,
|
|
30
|
+
type,
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export { analyze_package };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { log } from '../../../log.js';
|
|
2
|
+
import { cmd } from '../../../lib/cmd.js';
|
|
3
|
+
import { Configuration } from '../../../lib/Configuration.js';
|
|
4
|
+
|
|
5
|
+
const build_project = async (args) => {
|
|
6
|
+
const build_script = args.build_script;
|
|
7
|
+
const build_command = build_script
|
|
8
|
+
? `yarn run ${build_script}`
|
|
9
|
+
: Configuration.instance().buildCommand;
|
|
10
|
+
if (build_command) {
|
|
11
|
+
const cwd = args.cwd || process.cwd();
|
|
12
|
+
log.info(`⚙️ Building project [${build_command}]...`);
|
|
13
|
+
const timeout = 1000 * 60 * 100; // 100 minute timeout
|
|
14
|
+
await cmd(build_command, {
|
|
15
|
+
timeout,
|
|
16
|
+
cwd,
|
|
17
|
+
enableOutput: true,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
log.info(`⚡️ No build step`);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export { build_project };
|
package/dist/{builder/node/prepare_dockerfile.js → commands/deploy/node-pipeline/bundle_docker.js}
RENAMED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
import { log } from '../../../log.js';
|
|
2
|
+
import { cmd } from '../../../lib/cmd.js';
|
|
1
3
|
import fs from 'fs-extra';
|
|
2
4
|
import Handlebars from 'handlebars';
|
|
3
5
|
import * as path from 'path';
|
|
4
6
|
import { fileURLToPath } from 'url';
|
|
7
|
+
import { Configuration } from '../../../lib/Configuration.js';
|
|
5
8
|
|
|
6
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
10
|
const __dirname = path.dirname(__filename);
|
|
@@ -21,18 +24,20 @@ Handlebars.registerHelper("escape", function (variable) {
|
|
|
21
24
|
// Docker template file
|
|
22
25
|
const docker_template = Handlebars.compile(dockerfile);
|
|
23
26
|
const entrypoint_template = Handlebars.compile(entrypoint);
|
|
24
|
-
const
|
|
25
|
-
const { app, workdir,
|
|
26
|
-
const { template_context } = params;
|
|
27
|
+
const bundle_docker = async (props) => {
|
|
28
|
+
const { app, workdir, template_context } = props;
|
|
27
29
|
const entrypoint_custom = entrypoint_template(template_context);
|
|
28
|
-
const start_command =
|
|
29
|
-
log.info(
|
|
30
|
+
const start_command = Configuration.instance().startCommand;
|
|
31
|
+
log.info(`⚙️ Start command: ${start_command}`);
|
|
30
32
|
const dockerfile = docker_template({
|
|
31
33
|
from: template_context.from,
|
|
32
34
|
entry_script: entrypoint_custom,
|
|
33
35
|
start_command,
|
|
34
36
|
});
|
|
35
|
-
|
|
37
|
+
log.info(`📦 Packaging inside a docker image`);
|
|
38
|
+
// Build options
|
|
39
|
+
const timeout = 10 * 60 * 1000; // 10 minute timeout
|
|
40
|
+
await cmd(`docker build -t ${app.id} ${workdir} -f -<<EOF\n${dockerfile}\nEOF`, { timeout, enableOutput: true });
|
|
36
41
|
};
|
|
37
42
|
|
|
38
|
-
export {
|
|
43
|
+
export { bundle_docker };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { bundle_docker } from './bundle_docker.js';
|
|
2
|
+
import { analyze_package } from './analyze_package.js';
|
|
3
|
+
import { build_project } from './build_project.js';
|
|
4
|
+
|
|
5
|
+
const build_node = async (app, options) => {
|
|
6
|
+
// log.info(`🚀 Build Toolchain ${app.name} [${app.id}]`);
|
|
7
|
+
const { workdir, runtime } = options;
|
|
8
|
+
if (!runtime.version) {
|
|
9
|
+
throw new Error("Runtime version not specified for node");
|
|
10
|
+
}
|
|
11
|
+
// Analyze package.json to check if build is needed
|
|
12
|
+
const { build_script, type } = await analyze_package({ workdir });
|
|
13
|
+
await build_project({ app, build_script });
|
|
14
|
+
// Bundle project inside a docker image
|
|
15
|
+
await bundle_docker({
|
|
16
|
+
app,
|
|
17
|
+
workdir,
|
|
18
|
+
template_context: {
|
|
19
|
+
from: `node:${runtime.version}`,
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
return { type };
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export { build_node };
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { strategy_nodejs } from './strategies/nodejs.js';
|
|
2
2
|
import * as R from 'ramda';
|
|
3
3
|
import { has_any_of_files } from './helpers/has_any_of_files.js';
|
|
4
|
-
import { strategy_python } from './strategies/python.js';
|
|
5
4
|
import { strategy_docker } from './strategies/docker.js';
|
|
6
5
|
|
|
7
6
|
const runtime_detection = async (workdir) => {
|
|
8
7
|
const has = R.curry(has_any_of_files);
|
|
9
8
|
const strategy = R.cond([
|
|
10
9
|
[has(["package.json"]), R.always(strategy_nodejs)],
|
|
11
|
-
[has(["requirements.txt"]), R.always(strategy_python)],
|
|
10
|
+
// [has(["requirements.txt"]), R.always(strategy_python)],
|
|
12
11
|
[has(["Dockerfile"]), R.always(strategy_docker)],
|
|
13
12
|
])(workdir);
|
|
14
13
|
if (!strategy) {
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import path__default from 'path';
|
|
3
|
+
import { cmd } from '../../../../lib/cmd.js';
|
|
4
|
+
import { log } from '../../../../log.js';
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* Strategy to detect app name from package.json
|
|
@@ -10,13 +12,29 @@ import path__default from 'path';
|
|
|
10
12
|
const strategy_nodejs = async (workdir) => {
|
|
11
13
|
const packageJSONFile = path__default.join(workdir, "package.json");
|
|
12
14
|
// Check we have a valid name
|
|
13
|
-
const { name } = fs.readJSONSync(packageJSONFile);
|
|
15
|
+
const { name, engines } = fs.readJSONSync(packageJSONFile);
|
|
14
16
|
if (!name) {
|
|
15
17
|
throw new Error("Missing name in package.json");
|
|
16
18
|
}
|
|
19
|
+
// Use engines.node if found
|
|
20
|
+
let runtime_version = "18.19.0";
|
|
21
|
+
if (engines?.node) {
|
|
22
|
+
try {
|
|
23
|
+
const check_cmd = `npm view node@"${engines.node}" version | tail -n 1 | cut -d "'" -f2`;
|
|
24
|
+
const out = await cmd(check_cmd);
|
|
25
|
+
runtime_version = out.stdout.toString();
|
|
26
|
+
log.info(`Using node@${runtime_version} from engines in package.json (${engines.node})`);
|
|
27
|
+
}
|
|
28
|
+
catch (e) {
|
|
29
|
+
throw new Error(`Node version is not valid (${engines.node})`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
17
32
|
return {
|
|
18
33
|
app_name: name,
|
|
19
|
-
runtime:
|
|
34
|
+
runtime: {
|
|
35
|
+
name: "node",
|
|
36
|
+
version: runtime_version,
|
|
37
|
+
},
|
|
20
38
|
};
|
|
21
39
|
};
|
|
22
40
|
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { deploy } from './commands/deploy/index.js';
|
|
|
6
6
|
import { log } from './log.js';
|
|
7
7
|
import { init } from './commands/init/index.js';
|
|
8
8
|
import { version } from './config.js';
|
|
9
|
+
import { Configuration } from './lib/Configuration.js';
|
|
9
10
|
|
|
10
11
|
const yg = yargs();
|
|
11
12
|
yg.scriptName("faable")
|
|
@@ -17,6 +18,16 @@ yg.scriptName("faable")
|
|
|
17
18
|
description: "Path to the local `faable.json` file",
|
|
18
19
|
string: true,
|
|
19
20
|
})
|
|
21
|
+
.middleware(function (argv) {
|
|
22
|
+
if (argv.config) {
|
|
23
|
+
Configuration.instance().setConfigFile(argv.config, {
|
|
24
|
+
ignoreWarnings: false,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
Configuration.instance();
|
|
29
|
+
}
|
|
30
|
+
}, true)
|
|
20
31
|
.command(deploy)
|
|
21
32
|
.command(apps)
|
|
22
33
|
.command(configure)
|
|
@@ -3,16 +3,14 @@ import fs from 'fs-extra';
|
|
|
3
3
|
import { log } from '../log.js';
|
|
4
4
|
|
|
5
5
|
class Configuration {
|
|
6
|
-
workdir;
|
|
7
6
|
static _instance;
|
|
8
7
|
config = {};
|
|
9
|
-
constructor(
|
|
10
|
-
this.workdir = workdir;
|
|
8
|
+
constructor() {
|
|
11
9
|
// Try to read default config file
|
|
12
10
|
this.setConfigFile("faable.json", { ignoreWarnings: true });
|
|
13
11
|
}
|
|
14
12
|
setConfigFile(file, options) {
|
|
15
|
-
const config_file = path__default.join(
|
|
13
|
+
const config_file = path__default.join(process.cwd(), file);
|
|
16
14
|
if (fs.existsSync(config_file)) {
|
|
17
15
|
this.config = fs.readJSONSync(config_file);
|
|
18
16
|
log.info(`Loaded configuration from: ${file}`);
|
|
@@ -23,14 +21,17 @@ class Configuration {
|
|
|
23
21
|
}
|
|
24
22
|
}
|
|
25
23
|
}
|
|
26
|
-
static instance(
|
|
24
|
+
static instance() {
|
|
27
25
|
if (!Configuration._instance) {
|
|
28
|
-
Configuration._instance = new Configuration(
|
|
26
|
+
Configuration._instance = new Configuration();
|
|
29
27
|
}
|
|
30
28
|
return Configuration._instance;
|
|
31
29
|
}
|
|
32
|
-
|
|
33
|
-
return this.config
|
|
30
|
+
get startCommand() {
|
|
31
|
+
return this.config.startCommand || "npm run start";
|
|
32
|
+
}
|
|
33
|
+
get buildCommand() {
|
|
34
|
+
return this.config.buildCommand;
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
|
package/dist/log.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import pino from 'pino';
|
|
2
|
-
import
|
|
2
|
+
import 'pino-pretty';
|
|
3
3
|
|
|
4
|
-
const log = pino(
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
4
|
+
const log = pino({
|
|
5
|
+
transport: {
|
|
6
|
+
target: "pino-pretty",
|
|
7
|
+
options: {
|
|
8
|
+
colorize: true,
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
});
|
|
10
12
|
|
|
11
13
|
export { log };
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"fs-extra": "^11.1.0",
|
|
7
7
|
"handlebars": "^4.7.7",
|
|
8
8
|
"pino": "^8.11.0",
|
|
9
|
-
"pino-pretty": "^
|
|
9
|
+
"pino-pretty": "^9.4.0",
|
|
10
10
|
"promisify-child-process": "^4.1.1",
|
|
11
11
|
"prompts": "^2.4.2",
|
|
12
12
|
"ramda": "^0.29.0",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"yaml": "^2.2.2",
|
|
15
15
|
"yargs": "^17.6.2"
|
|
16
16
|
},
|
|
17
|
-
"version": "1.
|
|
17
|
+
"version": "1.5.0",
|
|
18
18
|
"bin": {
|
|
19
19
|
"faable": "bin/faable.js"
|
|
20
20
|
},
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import path__default from 'path';
|
|
2
|
-
import fs from 'fs-extra';
|
|
3
|
-
|
|
4
|
-
const builder = async (ctx) => {
|
|
5
|
-
const { app, workdir } = ctx;
|
|
6
|
-
const f = path__default.join(path__default.resolve(workdir), "Dockerfile");
|
|
7
|
-
return { dockerfile: (await fs.readFile(f)).toString() };
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
export { builder, builder as default };
|
package/dist/builder/index.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
const builders = {
|
|
2
|
-
node: () => import('./node/index.js'),
|
|
3
|
-
python: () => import('./python/index.js'),
|
|
4
|
-
docker: () => import('./docker/index.js'),
|
|
5
|
-
};
|
|
6
|
-
const import_builder = async (runtime_name) => {
|
|
7
|
-
try {
|
|
8
|
-
let builder_module = await builders[runtime_name]();
|
|
9
|
-
return builder_module.default;
|
|
10
|
-
}
|
|
11
|
-
catch (e) {
|
|
12
|
-
console.log(e);
|
|
13
|
-
throw new Error(`Builder import error for ${runtime_name}`);
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export { import_builder };
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path__default from 'path';
|
|
3
|
-
|
|
4
|
-
const analyze_package = async (ctx) => {
|
|
5
|
-
const { log, workdir } = ctx;
|
|
6
|
-
const node_modules_dir = path__default.join(path__default.resolve(workdir), "node_modules");
|
|
7
|
-
const installedModules = await fs.pathExists(node_modules_dir);
|
|
8
|
-
if (!installedModules) {
|
|
9
|
-
throw new Error("node_modules not found, please install packages first");
|
|
10
|
-
}
|
|
11
|
-
const package_file = path__default.join(path__default.resolve(workdir), "package.json");
|
|
12
|
-
const pkg = await fs.readJSON(package_file);
|
|
13
|
-
// Detect deployment type
|
|
14
|
-
let type = "node";
|
|
15
|
-
if (pkg.dependencies["next"]) {
|
|
16
|
-
type = "next";
|
|
17
|
-
log.info(`✅ Next.js ⚡️ project detected`);
|
|
18
|
-
}
|
|
19
|
-
return {
|
|
20
|
-
pkg,
|
|
21
|
-
type,
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export { analyze_package };
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { cmd } from '../../lib/cmd.js';
|
|
2
|
-
|
|
3
|
-
const build_project = async (ctx, nodeCtx) => {
|
|
4
|
-
const { log, config, workdir } = ctx;
|
|
5
|
-
// Check if exists any type of builc command
|
|
6
|
-
let build_command = config.getConfigProperty("buildCommand", process.env.FAABLE_NPM_BUILD_COMMAND);
|
|
7
|
-
const build_script_name = config.getConfigProperty("buildScript", "build");
|
|
8
|
-
const build_script = nodeCtx.pkg.scripts[build_script_name];
|
|
9
|
-
// No build command but build script
|
|
10
|
-
if (!build_command && build_script) {
|
|
11
|
-
build_command = `npm run ${build_script_name}`;
|
|
12
|
-
}
|
|
13
|
-
if (build_command) {
|
|
14
|
-
log.info(`✅ Build command detected. Running "${build_command}"`);
|
|
15
|
-
const timeout = 1000 * 60 * 100; // 100 minute timeout
|
|
16
|
-
await cmd(build_command, {
|
|
17
|
-
timeout,
|
|
18
|
-
cwd: workdir,
|
|
19
|
-
enableOutput: true,
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
else {
|
|
23
|
-
log.info(`No build script in package.json`);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
export { build_project };
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path__default from 'path';
|
|
3
|
-
import { cmd } from '../../lib/cmd.js';
|
|
4
|
-
|
|
5
|
-
const engine_version = async (ctx) => {
|
|
6
|
-
const { workdir, log } = ctx;
|
|
7
|
-
const packageJSONFile = path__default.join(workdir, "package.json");
|
|
8
|
-
// Check we have a valid name
|
|
9
|
-
const { name, engines } = fs.readJSONSync(packageJSONFile);
|
|
10
|
-
if (!name) {
|
|
11
|
-
throw new Error("Missing name in package.json");
|
|
12
|
-
}
|
|
13
|
-
// Use engines.node if found
|
|
14
|
-
let version = "18.19.0";
|
|
15
|
-
if (engines?.node) {
|
|
16
|
-
try {
|
|
17
|
-
const check_cmd = `npm view node@"${engines.node}" version | tail -n 1 | cut -d "'" -f2`;
|
|
18
|
-
const out = await cmd(check_cmd);
|
|
19
|
-
version = out.stdout.toString().trim();
|
|
20
|
-
log.info(`✅ Engine "${engines.node}" from package.json resolved to node@${version}`);
|
|
21
|
-
}
|
|
22
|
-
catch (e) {
|
|
23
|
-
throw new Error(`Node version is not valid (${engines.node})`);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return {
|
|
27
|
-
version,
|
|
28
|
-
};
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export { engine_version };
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { prepare_dockerfile } from './prepare_dockerfile.js';
|
|
2
|
-
import { analyze_package } from './analyze_package.js';
|
|
3
|
-
import { build_project } from './build_project.js';
|
|
4
|
-
import { engine_version } from './engine_version.js';
|
|
5
|
-
|
|
6
|
-
const builder = async (ctx) => {
|
|
7
|
-
// log.info(`🚀 Build Toolchain ${app.name} [${app.id}]`);
|
|
8
|
-
const { version } = await engine_version(ctx);
|
|
9
|
-
// Analyze package.json to check if build is needed
|
|
10
|
-
const nodeCtx = await analyze_package(ctx);
|
|
11
|
-
await build_project(ctx, nodeCtx);
|
|
12
|
-
// Bundle project inside a docker image
|
|
13
|
-
const { dockerfile } = await prepare_dockerfile(ctx, {
|
|
14
|
-
template_context: {
|
|
15
|
-
from: `node:${version}`,
|
|
16
|
-
},
|
|
17
|
-
});
|
|
18
|
-
return {
|
|
19
|
-
dockerfile,
|
|
20
|
-
params: {
|
|
21
|
-
type: nodeCtx.type,
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
export { builder, builder as default };
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path__default from 'path';
|
|
3
|
-
import { prepare_dockerfile } from './prepare_dockerfile.js';
|
|
4
|
-
|
|
5
|
-
const builder = async (ctx) => {
|
|
6
|
-
const { workdir, log } = ctx;
|
|
7
|
-
const hasRequirements = fs.existsSync(path__default.join(path__default.resolve(workdir), "requirements.txt"));
|
|
8
|
-
if (!hasRequirements) {
|
|
9
|
-
throw new Error(`Missing requirements.txt`);
|
|
10
|
-
}
|
|
11
|
-
const { dockerfile } = await prepare_dockerfile(ctx);
|
|
12
|
-
return {
|
|
13
|
-
dockerfile,
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export { builder, builder as default };
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import Handlebars from 'handlebars';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import { fileURLToPath } from 'url';
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
-
const templates_dir = path.join(__dirname, "templates");
|
|
9
|
-
const dockerfile = fs.readFileSync(`${templates_dir}/Dockerfile`).toString();
|
|
10
|
-
const entrypoint = fs
|
|
11
|
-
.readFileSync(`${templates_dir}/entrypoint.sh`)
|
|
12
|
-
.toString("utf-8");
|
|
13
|
-
Handlebars.registerHelper("escape", function (variable) {
|
|
14
|
-
//const escaped_quotes = variable.replace(/(['"])/g, "\\$1");
|
|
15
|
-
const escaped_lines = variable
|
|
16
|
-
.replace(/(['`\\])/g, "\\$1")
|
|
17
|
-
.replace(/([$])/g, "\\$1");
|
|
18
|
-
return escaped_lines.split("\n").join("\\n");
|
|
19
|
-
//return escaped_lines.split("\n").join("\\n");
|
|
20
|
-
});
|
|
21
|
-
// Docker template file
|
|
22
|
-
const docker_template = Handlebars.compile(dockerfile);
|
|
23
|
-
const entrypoint_template = Handlebars.compile(entrypoint);
|
|
24
|
-
const prepare_dockerfile = async (ctx, params = {}) => {
|
|
25
|
-
const { app, workdir, log, config } = ctx;
|
|
26
|
-
const entrypoint_custom = entrypoint_template(params);
|
|
27
|
-
const start_command = config.getConfigProperty("startCommand");
|
|
28
|
-
if (!start_command) {
|
|
29
|
-
throw new Error(`Missing start command configuration`);
|
|
30
|
-
}
|
|
31
|
-
log.info(`✅ Start command set to "${start_command}"`);
|
|
32
|
-
const dockerfile = docker_template({
|
|
33
|
-
...params,
|
|
34
|
-
entry_script: entrypoint_custom,
|
|
35
|
-
start_command,
|
|
36
|
-
});
|
|
37
|
-
return { dockerfile };
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export { prepare_dockerfile };
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
FROM python:3.9-slim
|
|
2
|
-
|
|
3
|
-
RUN apt-get update && apt-get install -y locales locales-all && rm -rf /var/lib/apt/lists/*
|
|
4
|
-
|
|
5
|
-
RUN sed -i '/es_ES.UTF-8/s/^# //g' /etc/locale.gen && \
|
|
6
|
-
locale-gen
|
|
7
|
-
|
|
8
|
-
ENV LANG es_ES.UTF-8
|
|
9
|
-
ENV LANGUAGE es_ES:es
|
|
10
|
-
ENV LC_ALL es_ES.UTF-8
|
|
11
|
-
|
|
12
|
-
ENV PORT=80
|
|
13
|
-
ENV START_COMMAND="{{start_command}}"
|
|
14
|
-
|
|
15
|
-
WORKDIR /app
|
|
16
|
-
|
|
17
|
-
COPY requirements.txt .
|
|
18
|
-
|
|
19
|
-
RUN pip install --no-cache-dir -r requirements.txt
|
|
20
|
-
|
|
21
|
-
COPY . .
|
|
22
|
-
|
|
23
|
-
# Entrypoint stript
|
|
24
|
-
RUN echo '{{{escape entry_script}}}' >> entrypoint.sh
|
|
25
|
-
|
|
26
|
-
CMD ["/bin/sh", "./entrypoint.sh"]
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import path__default from 'path';
|
|
3
|
-
|
|
4
|
-
const strategy_python = async (workdir) => {
|
|
5
|
-
const runtime_config = path__default.join(workdir, "runtime.txt");
|
|
6
|
-
// Select runtime based on config
|
|
7
|
-
if (fs.existsSync(runtime_config)) {
|
|
8
|
-
const runtime_data = fs.readFileSync(runtime_config).toString();
|
|
9
|
-
if (!runtime_data.startsWith("python-")) {
|
|
10
|
-
throw new Error("runtime.txt must have runtime format with python-<version>");
|
|
11
|
-
}
|
|
12
|
-
runtime_data.split("-")[1];
|
|
13
|
-
}
|
|
14
|
-
return {
|
|
15
|
-
runtime: "python",
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export { strategy_python };
|
|
File without changes
|
|
File without changes
|