Skip to content

Commit

Permalink
Merge pull request #1245 from Genez-io/dev
Browse files Browse the repository at this point in the history
Release 2.3.0: Dev to Master
  • Loading branch information
vladiulianbogdan authored Jul 30, 2024
2 parents d103db8 + 2e4d53d commit acc13a7
Show file tree
Hide file tree
Showing 15 changed files with 897 additions and 545 deletions.
958 changes: 480 additions & 478 deletions package-lock.json

Large diffs are not rendered by default.

28 changes: 14 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "genezio",
"version": "2.2.0",
"version": "2.3.0",
"description": "Command line utility to interact with Genezio infrastructure.",
"exports": "./index.js",
"type": "module",
Expand Down Expand Up @@ -44,7 +44,7 @@
"@amplitude/analytics-node": "^1.3.5",
"@aws-sdk/client-cloudformation": "^3.609.0",
"@aws-sdk/client-s3": "^3.554.0",
"@babel/core": "^7.24.7",
"@babel/core": "^7.24.9",
"@babel/parser": "^7.24.4",
"@babel/plugin-proposal-decorators": "^7.24.7",
"@babel/preset-env": "^7.24.4",
Expand Down Expand Up @@ -75,15 +75,15 @@
"dir-compare": "^5.0.0",
"dotenv": "^16.4.5",
"esbuild": "^0.18.20",
"esbuild-node-externals": "^1.11.0",
"esbuild-node-externals": "^1.14.0",
"execa": "^9.3.0",
"express": "^4.19.2",
"fs-extra": "^11.2.0",
"glob": "^8.0.3",
"hash-it": "^6.0.0",
"http-proxy": "^1.18.1",
"inquirer": "^9.2.15",
"isomorphic-git": "^1.27.0",
"inquirer": "^10.0.1",
"isomorphic-git": "^1.27.1",
"latest-version": "^9.0.0",
"lodash": "^4.17.21",
"log-update": "^6.0.0",
Expand All @@ -102,7 +102,7 @@
"uuid": "^10.0.0",
"whatwg-mimetype": "~3.0.0",
"which": "^4.0.0",
"yaml": "^2.4.2",
"yaml": "^2.5.0",
"yaml-transmute": "^0.0.7",
"zod": "^3.23.8"
},
Expand Down Expand Up @@ -133,37 +133,37 @@
"@types/lodash": "^4.17.6",
"@types/mime-types": "^2.1.3",
"@types/mustache": "^4.2.5",
"@types/node": "^20.14.8",
"@types/node": "^20.14.11",
"@types/node-cron": "^3.0.11",
"@types/semver": "^7.5.8",
"@types/uuid": "^10.0.0",
"@types/whatwg-mimetype": "~3.0.0",
"@types/which": "^3.0.3",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/eslint-plugin": "^7.17.0",
"@typescript-eslint/parser": "^7.15.0",
"envify": "^4.1.0",
"eslint": "^8.55.0",
"husky": "^9.0.10",
"lint-staged": "15.2.2",
"pre-commit": "^1.2.2",
"prettier": "^3.2.5",
"prettier": "^3.3.3",
"shx": "^0.3.4",
"vitest": "^1.6.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.18.0",
"@rollup/rollup-android-arm-eabi": "4.19.0",
"@rollup/rollup-android-arm64": "4.18.0",
"@rollup/rollup-darwin-arm64": "4.18.0",
"@rollup/rollup-darwin-x64": "4.18.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.18.0",
"@rollup/rollup-linux-arm64-gnu": "4.17.2",
"@rollup/rollup-linux-arm64-gnu": "4.19.0",
"@rollup/rollup-linux-arm64-musl": "4.18.0",
"@rollup/rollup-linux-riscv64-gnu": "4.18.0",
"@rollup/rollup-linux-x64-gnu": "4.18.0",
"@rollup/rollup-linux-x64-musl": "4.18.0",
"@rollup/rollup-win32-arm64-msvc": "4.18.0",
"@rollup/rollup-linux-x64-musl": "4.19.1",
"@rollup/rollup-win32-arm64-msvc": "4.19.1",
"@rollup/rollup-win32-ia32-msvc": "4.18.0",
"@rollup/rollup-win32-x64-msvc": "4.18.0"
"@rollup/rollup-win32-x64-msvc": "4.18.1"
},
"prettier": {
"tabWidth": 4,
Expand Down
3 changes: 3 additions & 0 deletions src/bundlers/node/nodeJsBundler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ export class NodeJsBundler implements BundlerInterface {
return (
file.extension !== ".ts" &&
file.extension !== ".js" &&
file.extension !== ".mjs" &&
file.extension !== ".cjs" &&
file.extension !== ".tsx" &&
file.extension !== ".jsx" &&
!file.path.includes("node_modules") &&
Expand Down Expand Up @@ -213,6 +215,7 @@ export class NodeJsBundler implements BundlerInterface {
outfile: outputFilePath,
plugins: [nodeExternalPlugin, supportRequireInESM],
sourcemap: "inline",
sourcesContent: false,
});

if (output.errors.length > 0) {
Expand Down
24 changes: 14 additions & 10 deletions src/commands/create/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,6 @@ export async function createCommand(options: GenezioCreateOptions) {
throw new UserError("Failed to create a Next.js project using create-next-app.");
});

await doAdaptiveLogAction("Finishing things up", () =>
transformVercelToGenezio(projectPath),
);

const yamlIOController = new YamlConfigurationIOController(
path.join(projectPath, "genezio.yaml"),
);
Expand All @@ -142,6 +138,10 @@ export async function createCommand(options: GenezioCreateOptions) {
yamlVersion: 2,
});

await doAdaptiveLogAction("Finishing things up", () =>
transformVercelToGenezio(projectPath),
).catch(() => {}); // Fail silently

log.info(SUCCESSFULL_CREATE_NEXTJS(projectPath));
break;
}
Expand Down Expand Up @@ -201,12 +201,16 @@ async function transformVercelToGenezio(projectPath: string) {
}

// Change Vercel favicon to Genezio favicon
const faviconPath = path.join(projectPath, "src", "app", "favicon.ico");
const genezioFaviconContent = await fetch(
"https://raw.githubusercontent.com/Genez-io/graphics/main/favicon/genezio.ico",
);
const genezioFaviconBuffer = await genezioFaviconContent.arrayBuffer();
nativeFs.writeFileSync(faviconPath, Buffer.from(genezioFaviconBuffer));
let faviconPath = path.join(projectPath, "src", "app", "favicon.ico");
if (!nativeFs.existsSync(faviconPath))
faviconPath = path.join(projectPath, "app", "favicon.ico");
if (nativeFs.existsSync(faviconPath)) {
const genezioFaviconContent = await fetch(
"https://raw.githubusercontent.com/Genez-io/graphics/main/favicon/genezio.ico",
);
const genezioFaviconBuffer = await genezioFaviconContent.arrayBuffer();
nativeFs.writeFileSync(faviconPath, Buffer.from(genezioFaviconBuffer));
}

// Replace the Vercel links with Genezio links
recursiveReplace(nativeFs, projectPath, [
Expand Down
22 changes: 20 additions & 2 deletions src/commands/deploy/genezio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ import {
} from "../../utils/jsProjectChecker.js";
import { YamlConfigurationIOController } from "../../yamlProjectConfiguration/v2.js";
import { FunctionType, Language } from "../../yamlProjectConfiguration/models.js";
import { runScript } from "../../utils/scripts.js";
import { expandFunctionURLVariablesFromScripts, runScript } from "../../utils/scripts.js";
import { scanClassesForDecorators } from "../../utils/configuration.js";
import configIOController, { YamlFrontend } from "../../yamlProjectConfiguration/v2.js";
import { ClusterCloudAdapter } from "../../cloudAdapter/cluster/clusterAdapter.js";
Expand All @@ -69,6 +69,8 @@ import { getCloudProvider } from "../../requests/getCloudProvider.js";
import fs from "fs";
import { getPresignedURLForProjectCodePush } from "../../requests/getPresignedURLForProjectCodePush.js";
import { uploadContentToS3 } from "../../requests/uploadContentToS3.js";
import { getProjectEnvFromProjectByName } from "../../requests/getProjectInfoByName.js";
import { kebabToCamelCase } from "../../utils/strings.js";

export async function genezioDeploy(options: GenezioDeployOptions) {
const configIOController = new YamlConfigurationIOController(options.config, {
Expand Down Expand Up @@ -675,7 +677,23 @@ export async function deployFrontend(

try {
await doAdaptiveLogAction(`Building frontend ${index + 1}`, async () => {
await runScript(frontend.scripts?.build, frontend.path);
// Get project environment details for a specific project/stage
const projectEnvDetails = await getProjectEnvFromProjectByName(name, stage);
if (!projectEnvDetails) {
throw new UserError("Project environment not found.");
}

// Transform function name from kebab-case (function-hello-world) to camelCase (functionHelloWorldApiUrl)
const functions = projectEnvDetails.functions?.map((f) => ({
name: kebabToCamelCase(f.name) + "ApiUrl",
url: f.cloudUrl,
}));

const expandedScripts = await expandFunctionURLVariablesFromScripts(
frontend.scripts?.build,
functions,
);
await runScript(expandedScripts, frontend.path);
});
} catch (error) {
if (error instanceof Error) log.error(new Error(error.message));
Expand Down
82 changes: 58 additions & 24 deletions src/commands/deploy/nextjs/deploy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
import { setEnvironmentVariables } from "../../../requests/setEnvironmentVariables.js";
import { GenezioCloudOutput } from "../../../cloudAdapter/cloudAdapter.js";
import {
ENVIRONMENT,
GENEZIO_FRONTEND_DEPLOYMENT_BUCKET,
NEXT_JS_GET_ACCESS_KEY,
NEXT_JS_GET_SECRET_ACCESS_KEY,
Expand All @@ -44,6 +45,7 @@ import {
import { getPresignedURLForProjectCodePush } from "../../../requests/getPresignedURLForProjectCodePush.js";
import { computeAssetsPaths } from "./assets.js";
import * as Sentry from "@sentry/node";
import { randomUUID } from "crypto";

export async function nextJsDeploy(options: GenezioDeployOptions) {
// Check if node_modules exists
Expand All @@ -53,28 +55,30 @@ export async function nextJsDeploy(options: GenezioDeployOptions) {
);
}

await writeOpenNextConfig();
const genezioConfig = await readOrAskConfig(options.config);

await writeOpenNextConfig(genezioConfig.region);
// Build the Next.js project
await $({ stdio: "inherit" })`npx --yes @genezio/open-next@latest build`.catch(() => {
throw new UserError("Failed to build the Next.js project. Check the logs above.");
});

const genezioConfig = await readOrAskConfig(options.config);
await checkProjectLimitations();

checkProjectLimitations();
const cacheToken = randomUUID();

const [deploymentResult, domainName] = await Promise.all([
// Deploy NextJs serverless functions
deployFunctions(genezioConfig, options.stage),
// Deploy NextJs static assets to S3
deployStaticAssets(genezioConfig, options.stage),
deployStaticAssets(genezioConfig, options.stage, cacheToken),
]);

const [, , cdnUrl] = await Promise.all([
// Upload the project code to S3 for in-browser editing
uploadUserCode(genezioConfig.name, genezioConfig.region, options.stage),
// Set environment variables for the Next.js project
setupEnvironmentVariables(deploymentResult, domainName, genezioConfig.region),
setupEnvironmentVariables(deploymentResult, domainName, genezioConfig.region, cacheToken),
// Deploy CDN that serves the Next.js app
deployCDN(deploymentResult.functions, domainName, genezioConfig, options.stage),
]);
Expand Down Expand Up @@ -160,13 +164,13 @@ async function uploadUserCode(name: string, region: string, stage: string): Prom
);
}

function checkProjectLimitations() {
async function checkProjectLimitations() {
const assetsPath = path.join(process.cwd(), ".open-next", "assets");
const fileList = fs.readdirSync(assetsPath);
const paths = await computeAssetsPaths(assetsPath, {} as CreateFrontendV2Origin);

if (fileList.length > 20) {
if (paths.length > 195) {
throw new UserError(
"We currently do not support having more than 20 files and folders within the public/ directory at the root level. As a workaround, you can organize some of these files into a subfolder.",
"We currently do not support having more than 195 files and folders within the public/ directory at the root level. As a workaround, you can organize some of these files into a subfolder.",
);
}
}
Expand All @@ -175,6 +179,7 @@ async function setupEnvironmentVariables(
deploymentResult: GenezioCloudOutput,
domainName: string,
region: string,
cacheToken: string,
) {
debugLogger.debug(`Setting Next.js environment variables, ${JSON.stringify(deploymentResult)}`);
await setEnvironmentVariables(deploymentResult.projectId, deploymentResult.projectEnvId, [
Expand All @@ -187,16 +192,12 @@ async function setupEnvironmentVariables(
value: GENEZIO_FRONTEND_DEPLOYMENT_BUCKET + "-" + region,
},
{
name: "CACHE_BUCKET_KEY_PREFIX",
value: `${domainName}/_cache`,
},
{
name: "CACHE_BUCKET_NAME",
value: GENEZIO_FRONTEND_DEPLOYMENT_BUCKET + "-" + region,
name: "GENEZIO_CACHE_TOKEN",
value: cacheToken,
},
{
name: "CACHE_BUCKET_REGION",
value: region,
name: "GENEZIO_DOMAIN_NAME",
value: domainName,
},
{
name: "AWS_ACCESS_KEY_ID",
Expand Down Expand Up @@ -282,7 +283,11 @@ async function deployCDN(
return distributionUrl;
}

async function deployStaticAssets(config: YamlProjectConfiguration, stage: string) {
async function deployStaticAssets(
config: YamlProjectConfiguration,
stage: string,
cacheToken: string,
) {
const getFrontendPresignedURLPromise = getFrontendPresignedURL(
/* subdomain= */ undefined,
/* projectName= */ config.name,
Expand All @@ -302,7 +307,7 @@ async function deployStaticAssets(config: YamlProjectConfiguration, stage: strin
),
fs.promises.cp(
path.join(process.cwd(), ".open-next", "cache"),
path.join(temporaryFolder, "next-static", "_cache"),
path.join(temporaryFolder, "next-static", cacheToken, "_cache"),
{ recursive: true },
),
]);
Expand Down Expand Up @@ -375,26 +380,55 @@ async function deployFunctions(config: YamlProjectConfiguration, stage?: string)
return result;
}

async function writeOpenNextConfig() {
async function writeOpenNextConfig(region: string) {
const OPEN_NEXT_CONFIG = `
import { IncrementalCache, Queue, TagCache } from "@genezio/nextjs-isr-${region}";
const deployment = process.env["GENEZIO_DOMAIN_NAME"] || "";
const token = (process.env["GENEZIO_CACHE_TOKEN"] || "") + "/_cache/" + (process.env["NEXT_BUILD_ID"] || "");
const queue = () => ({
name: "genezio-queue",
send: Queue.send.bind(null, deployment, token),
});
const incrementalCache = () => ({
name: "genezio-incremental-cache",
get: IncrementalCache.get.bind(null, deployment, token),
set: IncrementalCache.set.bind(null, deployment, token),
delete: IncrementalCache.delete.bind(null, deployment, token),
});
const tagCache = () => ({
name: "genzio-tag-cache",
getByTag: TagCache.getByTag.bind(null, deployment, token),
getByPath: TagCache.getByPath.bind(null, deployment, token),
getLastModified: TagCache.getLastModified.bind(null, deployment, token),
writeTags: TagCache.writeTags.bind(null, deployment, token),
});
const config = {
default: {
override: {
queue: "sqs-lite",
incrementalCache: "s3-lite",
tagCache: "dynamodb-lite",
queue,
incrementalCache,
tagCache,
},
},
imageOptimization: {
arch: "x64",
},
}
};
export default config;`;

// Write the open-next configuration
// TODO: Check if the file already exists and merge the configurations, instead of overwriting it.
const openNextConfigPath = path.join(process.cwd(), "open-next.config.ts");
await fs.promises.writeFile(openNextConfigPath, OPEN_NEXT_CONFIG);

const tag = ENVIRONMENT === "prod" ? "latest" : "dev";
await getPackageManager().install([`@genezio/nextjs-isr-${region}@${tag}`]);
}

async function readOrAskConfig(configPath: string): Promise<YamlProjectConfiguration> {
Expand Down
Loading

0 comments on commit acc13a7

Please sign in to comment.