Skip to content

Commit

Permalink
Merge pull request #1414 from Genez-io/release-2.6.2
Browse files Browse the repository at this point in the history
Release 2.6.2
  • Loading branch information
stefandarius authored Oct 8, 2024
2 parents 37bb6de + b96a6fa commit cc65232
Show file tree
Hide file tree
Showing 13 changed files with 971 additions and 612 deletions.
1,263 changes: 698 additions & 565 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "genezio",
"version": "2.6.1",
"version": "2.6.2",
"description": "Command line utility to interact with Genezio infrastructure.",
"exports": "./index.js",
"type": "module",
Expand Down Expand Up @@ -42,7 +42,7 @@
"license": "GPL-3",
"dependencies": {
"@amplitude/analytics-node": "^1.3.5",
"@aws-sdk/client-cloudformation": "^3.654.0",
"@aws-sdk/client-cloudformation": "^3.665.0",
"@aws-sdk/client-s3": "^3.637.0",
"@babel/core": "^7.25.2",
"@babel/parser": "^7.24.4",
Expand All @@ -52,7 +52,7 @@
"@babel/preset-typescript": "^7.24.6",
"@babel/traverse": "^7.24.1",
"@genezio/test-interface-component": "^1.2.3",
"@sentry/node": "^7.119.0",
"@sentry/node": "^7.119.1",
"@sentry/profiling-node": "~7.118.0",
"@types/adm-zip": "^0.5.5",
"@webcontainer/env": "^1.1.1",
Expand All @@ -64,7 +64,7 @@
"axios": "^1.7.5",
"body": "^5.1.0",
"body-parser": "^1.20.1",
"boxen": "^7.1.1",
"boxen": "^8.0.1",
"chokidar": "^3.6.0",
"cli-progress": "^3.12.0",
"cli-spinner": "^0.2.10",
Expand Down Expand Up @@ -96,7 +96,7 @@
"node-cron": "^3.0.3",
"open": "^10.1.0",
"ora": "^8.0.1",
"semver": "^7.6.2",
"semver": "^7.6.3",
"tslog": "^4.9.2",
"typescript": "^5.6.2",
"unique-names-generator": "^4.7.1",
Expand Down Expand Up @@ -141,7 +141,7 @@
"@types/whatwg-mimetype": "~3.0.0",
"@types/which": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^7.17.0",
"@typescript-eslint/parser": "^7.15.0",
"@typescript-eslint/parser": "^7.18.0",
"envify": "^4.1.0",
"eslint": "^8.55.0",
"husky": "^9.0.10",
Expand All @@ -156,12 +156,12 @@
"@rollup/rollup-android-arm64": "4.21.3",
"@rollup/rollup-darwin-arm64": "4.21.3",
"@rollup/rollup-darwin-x64": "4.18.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.21.2",
"@rollup/rollup-linux-arm64-gnu": "4.19.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.24.0",
"@rollup/rollup-linux-arm64-gnu": "4.22.5",
"@rollup/rollup-linux-arm64-musl": "4.22.4",
"@rollup/rollup-linux-riscv64-gnu": "4.21.2",
"@rollup/rollup-linux-x64-gnu": "4.18.0",
"@rollup/rollup-linux-x64-musl": "4.21.0",
"@rollup/rollup-linux-x64-gnu": "4.22.5",
"@rollup/rollup-linux-x64-musl": "4.24.0",
"@rollup/rollup-win32-arm64-msvc": "4.21.2",
"@rollup/rollup-win32-ia32-msvc": "4.22.4",
"@rollup/rollup-win32-x64-msvc": "4.21.2"
Expand Down
9 changes: 9 additions & 0 deletions src/cloudAdapter/cloudAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,18 @@ export enum GenezioCloudInputType {
FUNCTION = "function",
}

export type GenezioFunctionMetadata = {
cwd: string;
cmd: string;
http_port: string;
};

export type GenezioCloudInput =
| {
type: GenezioCloudInputType.CLASS;
name: string;
archivePath: string;
archiveName?: string;
filePath: string;
methods: MethodConfiguration[];
dependenciesInfo?: Dependency[];
Expand All @@ -28,8 +35,10 @@ export type GenezioCloudInput =
type: GenezioCloudInputType.FUNCTION;
name: string;
archivePath: string;
archiveName?: string;
entryFile: string;
unzippedBundleSize: number;
metadata?: GenezioFunctionMetadata;
};

export type GenezioCloudResultClass = {
Expand Down
11 changes: 9 additions & 2 deletions src/cloudAdapter/cluster/clusterAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { getFrontendPresignedURL } from "../../requests/getFrontendPresignedURL.
import { uploadContentToS3 } from "../../requests/uploadContentToS3.js";
import { createFrontendProject } from "../../requests/createFrontendProject.js";
import { UserError } from "../../errors.js";
import { createHash } from "../../utils/strings.js";

export class ClusterCloudAdapter implements CloudAdapter {
async deploy(
Expand Down Expand Up @@ -130,8 +131,14 @@ export class ClusterCloudAdapter implements CloudAdapter {
frontend: YamlFrontend,
stage: string,
): Promise<string> {
const finalStageName = stage != "" && stage != "prod" ? `-${stage}` : "";
const finalSubdomain = frontend.subdomain + finalStageName;
const finalStageName =
stage != "" && stage != "prod" ? `-${stage.replaceAll(/[/_.]/gm, "-")}` : "";
let finalSubdomain = frontend.subdomain + finalStageName;
debugLogger.debug("Subdomain:", finalSubdomain);
if (finalSubdomain.length > 63) {
debugLogger.debug("Subdomain is too long. Generating random subdomain.");
finalSubdomain = frontend.subdomain?.substring(0, 55) + "-" + createHash(stage, 4);
}
const archivePath = path.join(await createTemporaryFolder(), `${finalSubdomain}.zip`);
debugLogger.debug("Creating temporary folder", archivePath);

Expand Down
17 changes: 12 additions & 5 deletions src/cloudAdapter/genezio/genezioAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { calculateBiggestFiles } from "../../utils/calculateBiggestProjectFiles.
import Table from "cli-table";
import { UserError } from "../../errors.js";
import { stdout } from "process";
import { createHash } from "../../utils/strings.js";

const BUNDLE_SIZE_LIMIT = 256901120;
async function handleBigElementSizeError(
Expand All @@ -37,7 +38,7 @@ async function handleBigElementSizeError(
const size = await getFileSize(element.archivePath);
if (size > BUNDLE_SIZE_LIMIT) {
throw new UserError(
`Your class ${element.name} is too big: ${size} bytes. The maximum size is 250MB. Try to reduce the size of your class.`,
`Your ${element.type} ${element.name} is too big: ${size} bytes. The maximum size is 250MB. Try to reduce the size of your ${element.type}.`,
);
}

Expand All @@ -64,7 +65,7 @@ async function handleBigElementSizeError(
// Throw this error if bundle size is too big and the user is not using js or ts files.
if (!dependenciesInfo || !allNonJsFilesPaths) {
throw new UserError(
`Your class ${element.name} is too big: ${element.unzippedBundleSize} bytes. The maximum size is 250MB. Try to reduce the size of your class.`,
`Your ${element.type} ${element.name} is too big: ${element.unzippedBundleSize} bytes. The maximum size is 250MB. Try to reduce the size of your class.`,
);
}

Expand Down Expand Up @@ -134,10 +135,12 @@ export class GenezioCloudAdapter implements CloudAdapter {
const promisesDeploy = input.map(async (element) => {
await handleBigElementSizeError(element, projectConfiguration, BUNDLE_SIZE_LIMIT);

debugLogger.debug(`Get the presigned URL for ${element.type}: ${element.name}.`);
debugLogger.debug(
`Get the presigned URL for ${element.type}: ${element.name} ${element.archiveName}.`,
);
const presignedUrl = await getPresignedURL(
projectConfiguration.region,
"genezioDeploy.zip",
element.archiveName ?? "genezioDeploy.zip",
projectConfiguration.name,
element.name,
);
Expand Down Expand Up @@ -205,7 +208,11 @@ export class GenezioCloudAdapter implements CloudAdapter {
): Promise<string> {
const finalStageName =
stage != "" && stage != "prod" ? `-${stage.replaceAll(/[/_.]/gm, "-")}` : "";
const finalSubdomain = frontend.subdomain + finalStageName;
let finalSubdomain = frontend.subdomain + finalStageName;
if (finalSubdomain.length > 63) {
debugLogger.debug("Subdomain is too long. Generating random subdomain.");
finalSubdomain = frontend.subdomain?.substring(0, 55) + "-" + createHash(stage, 4);
}
const archivePath = path.join(await createTemporaryFolder(), `${finalSubdomain}.zip`);
debugLogger.debug("Creating temporary folder", archivePath);

Expand Down
10 changes: 10 additions & 0 deletions src/commands/deploy/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import fs from "fs";
import { nextJsDeploy } from "./nextjs/deploy.js";
import path from "path";
import { nuxtNitroDeploy } from "./nuxt/deploy.js";
import { dockerDeploy } from "./docker/deploy.js";

export async function deployCommand(options: GenezioDeployOptions) {
await interruptLocalProcesses();
Expand All @@ -26,6 +27,10 @@ export async function deployCommand(options: GenezioDeployOptions) {
debugLogger.debug("Deploying Nuxt app");
await nuxtNitroDeploy(options, type);
break;
case DeployType.Docker:
debugLogger.debug("Deploying Docker app");
await dockerDeploy(options);
break;
}
}

Expand All @@ -34,6 +39,7 @@ export enum DeployType {
NextJS,
Nitro,
Nuxt,
Docker,
}

function decideDeployType(): DeployType {
Expand Down Expand Up @@ -83,5 +89,9 @@ function decideDeployType(): DeployType {
}
}

if (fs.existsSync("Dockerfile")) {
return DeployType.Docker;
}

return DeployType.Classic;
}
171 changes: 171 additions & 0 deletions src/commands/deploy/docker/deploy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { $ } from "execa";
import { UserError } from "../../../errors.js";
import { debugLogger, log } from "../../../utils/logging.js";
import { readOrAskConfig } from "../utils.js";
import { GenezioDeployOptions } from "../../../models/commandOptions.js";
import {
FunctionConfiguration,
ProjectConfiguration,
} from "../../../models/projectConfiguration.js";
import { CloudProviderIdentifier } from "../../../models/cloudProviderIdentifier.js";
import { getCloudProvider } from "../../../requests/getCloudProvider.js";
import { getCloudAdapter } from "../genezio.js";
import { GenezioCloudInputType } from "../../../cloudAdapter/cloudAdapter.js";
import { setEnvironmentVariables } from "../../../requests/setEnvironmentVariables.js";
import { FunctionType } from "../../../projectConfiguration/yaml/models.js";
import { createTemporaryFolder } from "../../../utils/file.js";
import path from "path";
import { reportSuccessFunctions } from "../../../utils/reporter.js";

export async function dockerDeploy(options: GenezioDeployOptions) {
const config = await readOrAskConfig(options.config);
const projectConfiguration = new ProjectConfiguration(
config,
CloudProviderIdentifier.GENEZIO_CLOUD,
{
generatorResponses: [],
classesInfo: [],
},
);

log.info("Check docker version...");
await $({ stdio: "inherit" })`docker --version`.catch((err) => {
debugLogger.error(err);
throw new UserError("Docker is not installed. Please install Docker and try again.");
});

log.info("Building image...");
await $({
stdio: "inherit",
})`docker buildx build --platform=linux/amd64 -t ${config.name} .`.catch((err) => {
debugLogger.error(err);
throw new UserError("Failed to build Docker image.");
});

log.info("Creating the container...");
const { stdout } = await $`docker create --name genezio-${config.name} ${config.name}`.catch(
(err) => {
debugLogger.error(err);
throw new UserError("Failed to create the container.");
},
);
const containerId = await extractContainerId(stdout);
const tempFolder = await createTemporaryFolder();
const archivePath = path.join(tempFolder, `genezio-${config.name}.tar`);

log.info("Exporting the container...");
await $({
stdio: "inherit",
shell: true,
})`docker export genezio-${config.name} > ${archivePath}`.catch((err) => {
debugLogger.error(err);
throw new UserError("Failed to export the container.");
});

const { stdout: stdoutInspect } = await $`docker inspect ${containerId}`.catch((err) => {
debugLogger.error(err);
throw new UserError("Failed to inspect the container.");
});

await $({ stdio: "inherit" })`docker container rm genezio-${config.name}`.catch((err) => {
debugLogger.error(err);
throw new UserError("Failed to remove the container.");
});

const inspectResult = JSON.parse(stdoutInspect);
const envs = inspectResult[0].Config.Env;
const cmd = inspectResult[0].Config.Cmd;
const cwd = inspectResult[0].Config.WorkingDir;
const entrypoint = inspectResult[0].Config.Entrypoint;
const exposedPorts = inspectResult[0].Config.ExposedPorts;
let cmdEntryFile = "";
const port = getPort(exposedPorts);

if (entrypoint) {
cmdEntryFile += entrypoint.join(" ");
} else {
cmdEntryFile += "/bin/sh -c";
}

if (cmd) {
cmdEntryFile += cmd.join(" ");
}

projectConfiguration.functions.push(
new FunctionConfiguration(
/*name:*/ "docker-container",
/*path:*/ "",
/*handler:*/ "",
/*language:*/ "container",
/*entry*/ "",
/*type:*/ FunctionType.aws,
),
);

const cloudProvider = await getCloudProvider(projectConfiguration.name);
const cloudAdapter = getCloudAdapter(cloudProvider);
const result = await cloudAdapter.deploy(
[
{
type: GenezioCloudInputType.FUNCTION,
name: "docker-container",
archivePath,
archiveName: `genezio-${config.name}.tar`,
entryFile: cmdEntryFile,
unzippedBundleSize: 100,
metadata: {
cmd: cmdEntryFile,
cwd: cwd,
http_port: port,
},
},
],
projectConfiguration,
{
stage: options.stage,
},
["docker"],
);

const envVars = envs.map((env: string) => {
const components = env.split("=");
const key = components[0];
const value = components.slice(1).join("=");
return {
name: key,
value,
};
});

await setEnvironmentVariables(result.projectId, result.projectEnvId, envVars);

reportSuccessFunctions(result.functions);
}

function getPort(exposedPort: { [id: string]: string }): string {
let port = "8080";

if (Object.keys(exposedPort).length >= 2) {
throw new UserError("Only one port can be exposed.");
} else if (Object.keys(exposedPort).length === 1) {
port = Object.keys(exposedPort)[0].split("/")[0];
const protocol = Object.keys(exposedPort)[0].split("/")[1];
if (protocol !== "tcp") {
throw new UserError("Only TCP protocol is supported.");
}
}

return port;
}

async function extractContainerId(stdout: string): Promise<string> {
// Use a regular expression to match a 64-character hexadecimal string
const containerIdMatch = stdout.match(/[a-f0-9]{64}/i);

// If a match is found, return the first match (container ID)
if (containerIdMatch) {
return containerIdMatch[0];
} else {
throw new Error("Container ID not found in the output.");
}
}
2 changes: 1 addition & 1 deletion src/commands/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { GenezioTelemetry, TelemetryEventTypes } from "../telemetry/telemetry.js
import { isCI } from "../utils/process.js";

export async function loginCommand(accessToken: string, logSuccessMessage = true) {
if (logSuccessMessage) log.info(asciiCapybara);
if (!isCI() && logSuccessMessage) log.info(asciiCapybara);

await GenezioTelemetry.sendEvent({
eventType: TelemetryEventTypes.GENEZIO_LOGIN,
Expand Down
Loading

0 comments on commit cc65232

Please sign in to comment.