From 9464dce541eef12f18621103d3d7d536cfb18334 Mon Sep 17 00:00:00 2001 From: VPhung24 Date: Sat, 28 Sep 2024 22:43:27 +0000 Subject: [PATCH] wip: nextjs build --- .../build/build_webapp_snowball.py | 117 ++++++++++++++++++ .../cerc-nextjs-snowball/Dockerfile | 52 ++++++++ .../cerc-nextjs-snowball/build.sh | 15 +++ stack_orchestrator/deploy/webapp/util.py | 1 + stack_orchestrator/main.py | 4 +- 5 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 stack_orchestrator/build/build_webapp_snowball.py create mode 100644 stack_orchestrator/data/container-build/cerc-nextjs-snowball/Dockerfile create mode 100755 stack_orchestrator/data/container-build/cerc-nextjs-snowball/build.sh diff --git a/stack_orchestrator/build/build_webapp_snowball.py b/stack_orchestrator/build/build_webapp_snowball.py new file mode 100644 index 00000000..8719c826 --- /dev/null +++ b/stack_orchestrator/build/build_webapp_snowball.py @@ -0,0 +1,117 @@ +# Copyright © 2022, 2023 Vulcanize + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +# Builds webapp containers + +# env vars: +# CERC_REPO_BASE_DIR defaults to ~/cerc + +# TODO: display the available list of containers; allow re-build of either all or specific containers + +import os +import sys + +from decouple import config +import click +from pathlib import Path +from stack_orchestrator.build import build_containers +from stack_orchestrator.deploy.webapp.util import determine_base_container, TimedLogger +from stack_orchestrator.build.build_types import BuildContext + + +@click.command() +@click.option('--base-container') +@click.option('--source-repo', help="directory containing the webapp to build", required=True) +@click.option("--force-rebuild", is_flag=True, default=False, help="Override dependency checking -- always rebuild") +@click.option("--extra-build-args", help="Supply extra arguments to build") +@click.option("--tag", help="Container tag (default: cerc/:local)") +@click.pass_context +def command(ctx, base_container, source_repo, force_rebuild, extra_build_args, tag): + '''build the specified webapp container''' + logger = TimedLogger() + + quiet = ctx.obj.quiet + debug = ctx.obj.debug + verbose = ctx.obj.verbose + local_stack = ctx.obj.local_stack + stack = ctx.obj.stack + + # See: https://stackoverflow.com/questions/25389095/python-get-path-of-root-project-structure + container_build_dir = Path(__file__).absolute().parent.parent.joinpath("data", "container-build") + + if local_stack: + dev_root_path = os.getcwd()[0:os.getcwd().rindex("stack-orchestrator")] + logger.log(f'Local stack dev_root_path (CERC_REPO_BASE_DIR) overridden to: {dev_root_path}') + else: + dev_root_path = os.path.expanduser(config("CERC_REPO_BASE_DIR", default="~/cerc")) + + if verbose: + logger.log(f'Dev Root is: {dev_root_path}') + + if not base_container: + base_container = determine_base_container(source_repo) + + # First build the base container. + container_build_env = build_containers.make_container_build_env(dev_root_path, container_build_dir, debug, + force_rebuild, extra_build_args) + + if verbose: + logger.log(f"Building base container: {base_container}") + + build_context_1 = BuildContext( + stack, + base_container, + container_build_dir, + container_build_env, + dev_root_path, + ) + ok = build_containers.process_container(build_context_1) + if not ok: + logger.log("ERROR: Build failed.") + sys.exit(1) + + if verbose: + logger.log(f"Base container {base_container} build finished.") + + # Now build the target webapp. We use the same build script, but with a different Dockerfile and work dir. + container_build_env["CERC_WEBAPP_BUILD_RUNNING"] = "true" + container_build_env["CERC_CONTAINER_BUILD_WORK_DIR"] = os.path.abspath(source_repo) + container_build_env["CERC_CONTAINER_BUILD_DOCKERFILE"] = os.path.join(container_build_dir, + base_container.replace("/", "-"), + "Dockerfile") + if not tag: + webapp_name = os.path.abspath(source_repo).split(os.path.sep)[-1] + tag = f"cerc/{webapp_name}:local" + + container_build_env["CERC_CONTAINER_BUILD_TAG"] = tag + + if verbose: + logger.log(f"Building app container: {tag}") + + build_context_2 = BuildContext( + stack, + base_container, + container_build_dir, + container_build_env, + dev_root_path, + ) + ok = build_containers.process_container(build_context_2) + if not ok: + logger.log("ERROR: Build failed.") + sys.exit(1) + + if verbose: + logger.log(f"App container {base_container} build finished.") + logger.log("build-webapp-snowball complete", show_step_time=False, show_total_time=True) diff --git a/stack_orchestrator/data/container-build/cerc-nextjs-snowball/Dockerfile b/stack_orchestrator/data/container-build/cerc-nextjs-snowball/Dockerfile new file mode 100644 index 00000000..07855c4e --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-nextjs-snowball/Dockerfile @@ -0,0 +1,52 @@ +FROM node:18-alpine AS base + +# 1. Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat + +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./ +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i; \ + else echo "Lockfile not found." && exit 1; \ + fi + + +# 2. Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . +# This will do the trick, use the corresponding env file for each environment. +COPY .env.production.sample .env.production +RUN npm run build + +# 3. Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV=production + +RUN addgroup -g 1001 -S nodejs +RUN adduser -S nextjs -u 1001 + +COPY --from=builder /app/public ./public + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +# Expose port for http +EXPOSE 80 + +ENV PORT=80 + +CMD HOSTNAME="0.0.0.0" node server.js diff --git a/stack_orchestrator/data/container-build/cerc-nextjs-snowball/build.sh b/stack_orchestrator/data/container-build/cerc-nextjs-snowball/build.sh new file mode 100755 index 00000000..8d4e26ae --- /dev/null +++ b/stack_orchestrator/data/container-build/cerc-nextjs-snowball/build.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash +# Build cerc/cerc-nextjs-snowball + +source ${CERC_CONTAINER_BASE_DIR}/build-base.sh + +# See: https://stackoverflow.com/a/246128/1701505 +SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + +CERC_CONTAINER_BUILD_WORK_DIR=${CERC_CONTAINER_BUILD_WORK_DIR:-$SCRIPT_DIR} +CERC_CONTAINER_BUILD_DOCKERFILE=${CERC_CONTAINER_BUILD_DOCKERFILE:-$SCRIPT_DIR/Dockerfile} +CERC_CONTAINER_BUILD_TAG=${CERC_CONTAINER_BUILD_TAG:-cerc/nextjs-snowball:local} + +CERC_NPM_REGISTRY_URL="https://git.vdb.to/api/packages/cerc-io/npm/" + +docker build -t cerc/nextjs-snowball:local ${build_command_args} -f ${SCRIPT_DIR}/Dockerfile ${CERC_REPO_BASE_DIR}/nextjs-snowball diff --git a/stack_orchestrator/deploy/webapp/util.py b/stack_orchestrator/deploy/webapp/util.py index c6b6a786..b99ab652 100644 --- a/stack_orchestrator/deploy/webapp/util.py +++ b/stack_orchestrator/deploy/webapp/util.py @@ -482,6 +482,7 @@ def determine_base_container(clone_dir, app_type="webapp"): base_container = "cerc/webapp-base" if app_type == "webapp/next": + # base_container = "cerc/nextjs-snowball" base_container = "cerc/nextjs-base" elif app_type == "webapp": pkg_json_path = os.path.join(clone_dir, "package.json") diff --git a/stack_orchestrator/main.py b/stack_orchestrator/main.py index 5ae10468..b9ff86a7 100644 --- a/stack_orchestrator/main.py +++ b/stack_orchestrator/main.py @@ -21,6 +21,7 @@ from stack_orchestrator.repos import fetch_stack from stack_orchestrator.build import build_containers, fetch_containers from stack_orchestrator.build import build_npms from stack_orchestrator.build import build_webapp +from stack_orchestrator.build import build_webapp_snowball from stack_orchestrator.deploy.webapp import (run_webapp, deploy_webapp, deploy_webapp_from_registry, @@ -59,6 +60,7 @@ cli.add_command(build_containers.command, "build-containers") cli.add_command(fetch_containers.command, "fetch-containers") cli.add_command(build_npms.command, "build-npms") cli.add_command(build_webapp.command, "build-webapp") +cli.add_command(build_webapp_snowball.command, "build-webapp-snowball") cli.add_command(run_webapp.command, "run-webapp") cli.add_command(deploy_webapp.command, "deploy-webapp") cli.add_command(deploy_webapp_from_registry.command, "deploy-webapp-from-registry") @@ -69,4 +71,4 @@ cli.add_command(deploy.command, "deploy") # deploy is an alias for deploy-syste cli.add_command(deploy.command, "deploy-system") cli.add_command(deployment.command, "deployment") cli.add_command(version.command, "version") -cli.add_command(update.command, "update") +cli.add_command(update.command, "update") \ No newline at end of file -- 2.45.2