forked from cerc-io/stack-orchestrator
		
	Add new build-webapp command and related scripts and containers. (#626)
* Add new build-webapp command and related scripts and containers.
This commit is contained in:
		
							parent
							
								
									4456e70c93
								
							
						
					
					
						commit
						660326f713
					
				
							
								
								
									
										49
									
								
								.gitea/workflows/test-webapp.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								.gitea/workflows/test-webapp.yml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,49 @@ | |||||||
|  | name: Webapp Test | ||||||
|  | 
 | ||||||
|  | on: | ||||||
|  |   pull_request: | ||||||
|  |     branches: '*' | ||||||
|  |   push: | ||||||
|  |     branches: | ||||||
|  |       - main | ||||||
|  |       - ci-test | ||||||
|  |     paths-ignore: | ||||||
|  |       - '.gitea/workflows/triggers/*' | ||||||
|  | 
 | ||||||
|  | # Needed until we can incorporate docker startup into the executor container | ||||||
|  | env: | ||||||
|  |   DOCKER_HOST: unix:///var/run/dind.sock | ||||||
|  | 
 | ||||||
|  | jobs: | ||||||
|  |   test: | ||||||
|  |     name: "Run webapp test suite" | ||||||
|  |     runs-on: ubuntu-latest | ||||||
|  |     steps: | ||||||
|  |       - name: "Clone project repository" | ||||||
|  |         uses: actions/checkout@v3 | ||||||
|  |       # At present the stock setup-python action fails on Linux/aarch64 | ||||||
|  |       # Conditional steps below workaroud this by using deadsnakes for that case only | ||||||
|  |       - name: "Install Python for ARM on Linux" | ||||||
|  |         if: ${{ runner.arch == 'arm64' && runner.os == 'Linux' }} | ||||||
|  |         uses: deadsnakes/action@v3.0.1 | ||||||
|  |         with: | ||||||
|  |           python-version: '3.8' | ||||||
|  |       - name: "Install Python cases other than ARM on Linux" | ||||||
|  |         if: ${{ ! (runner.arch == 'arm64' && runner.os == 'Linux') }} | ||||||
|  |         uses: actions/setup-python@v4 | ||||||
|  |         with: | ||||||
|  |           python-version: '3.8' | ||||||
|  |       - name: "Print Python version" | ||||||
|  |         run: python3 --version | ||||||
|  |       - name: "Install shiv" | ||||||
|  |         run: pip install shiv | ||||||
|  |       - name: "Generate build version file" | ||||||
|  |         run: ./scripts/create_build_tag_file.sh | ||||||
|  |       - name: "Build local shiv package" | ||||||
|  |         run: ./scripts/build_shiv_package.sh | ||||||
|  |       - name: Start dockerd # Also needed until we can incorporate into the executor | ||||||
|  |         run: | | ||||||
|  |           dockerd -H $DOCKER_HOST --userland-proxy=false & | ||||||
|  |           sleep 5 | ||||||
|  |       - name: "Run webapp tests" | ||||||
|  |         run: ./tests/webapp-test/run-webapp-test.sh | ||||||
| @ -33,6 +33,73 @@ from stack_orchestrator.base import get_npm_registry_url | |||||||
| # TODO: find a place for this | # TODO: find a place for this | ||||||
| #    epilog="Config provided either in .env or settings.ini or env vars: CERC_REPO_BASE_DIR (defaults to ~/cerc)" | #    epilog="Config provided either in .env or settings.ini or env vars: CERC_REPO_BASE_DIR (defaults to ~/cerc)" | ||||||
| 
 | 
 | ||||||
|  | def make_container_build_env(dev_root_path: str, | ||||||
|  |                              container_build_dir: str, | ||||||
|  |                              debug: bool, | ||||||
|  |                              force_rebuild: bool, | ||||||
|  |                              extra_build_args: str): | ||||||
|  |     container_build_env = { | ||||||
|  |         "CERC_NPM_REGISTRY_URL": get_npm_registry_url(), | ||||||
|  |         "CERC_GO_AUTH_TOKEN": config("CERC_GO_AUTH_TOKEN", default=""), | ||||||
|  |         "CERC_NPM_AUTH_TOKEN": config("CERC_NPM_AUTH_TOKEN", default=""), | ||||||
|  |         "CERC_REPO_BASE_DIR": dev_root_path, | ||||||
|  |         "CERC_CONTAINER_BASE_DIR": container_build_dir, | ||||||
|  |         "CERC_HOST_UID": f"{os.getuid()}", | ||||||
|  |         "CERC_HOST_GID": f"{os.getgid()}", | ||||||
|  |         "DOCKER_BUILDKIT": config("DOCKER_BUILDKIT", default="0") | ||||||
|  |     } | ||||||
|  |     container_build_env.update({"CERC_SCRIPT_DEBUG": "true"} if debug else {}) | ||||||
|  |     container_build_env.update({"CERC_FORCE_REBUILD": "true"} if force_rebuild else {}) | ||||||
|  |     container_build_env.update({"CERC_CONTAINER_EXTRA_BUILD_ARGS": extra_build_args} if extra_build_args else {}) | ||||||
|  |     docker_host_env = os.getenv("DOCKER_HOST") | ||||||
|  |     if docker_host_env: | ||||||
|  |         container_build_env.update({"DOCKER_HOST": docker_host_env}) | ||||||
|  | 
 | ||||||
|  |     return container_build_env | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | def process_container(container, | ||||||
|  |                       container_build_dir: str, | ||||||
|  |                       container_build_env: dict, | ||||||
|  |                       dev_root_path: str, | ||||||
|  |                       quiet: bool, | ||||||
|  |                       verbose: bool, | ||||||
|  |                       dry_run: bool, | ||||||
|  |                       continue_on_error: bool, | ||||||
|  |                       ): | ||||||
|  |     if not quiet: | ||||||
|  |         print(f"Building: {container}") | ||||||
|  |     build_dir = os.path.join(container_build_dir, container.replace("/", "-")) | ||||||
|  |     build_script_filename = os.path.join(build_dir, "build.sh") | ||||||
|  |     if verbose: | ||||||
|  |         print(f"Build script filename: {build_script_filename}") | ||||||
|  |     if os.path.exists(build_script_filename): | ||||||
|  |         build_command = build_script_filename | ||||||
|  |     else: | ||||||
|  |         if verbose: | ||||||
|  |             print(f"No script file found: {build_script_filename}, using default build script") | ||||||
|  |         repo_dir = container.split('/')[1] | ||||||
|  |         # TODO: make this less of a hack -- should be specified in some metadata somewhere | ||||||
|  |         # Check if we have a repo for this container. If not, set the context dir to the container-build subdir | ||||||
|  |         repo_full_path = os.path.join(dev_root_path, repo_dir) | ||||||
|  |         repo_dir_or_build_dir = repo_full_path if os.path.exists(repo_full_path) else build_dir | ||||||
|  |         build_command = os.path.join(container_build_dir, | ||||||
|  |                                      "default-build.sh") + f" {container}:local {repo_dir_or_build_dir}" | ||||||
|  |     if not dry_run: | ||||||
|  |         if verbose: | ||||||
|  |             print(f"Executing: {build_command} with environment: {container_build_env}") | ||||||
|  |         build_result = subprocess.run(build_command, shell=True, env=container_build_env) | ||||||
|  |         if verbose: | ||||||
|  |             print(f"Return code is: {build_result.returncode}") | ||||||
|  |         if build_result.returncode != 0: | ||||||
|  |             print(f"Error running build for {container}") | ||||||
|  |             if not continue_on_error: | ||||||
|  |                 print("FATAL Error: container build failed and --continue-on-error not set, exiting") | ||||||
|  |                 sys.exit(1) | ||||||
|  |             else: | ||||||
|  |                 print("****** Container Build Error, continuing because --continue-on-error is set") | ||||||
|  |     else: | ||||||
|  |         print("Skipped") | ||||||
| 
 | 
 | ||||||
| @click.command() | @click.command() | ||||||
| @click.option('--include', help="only build these containers") | @click.option('--include', help="only build these containers") | ||||||
| @ -83,61 +150,16 @@ def command(ctx, include, exclude, force_rebuild, extra_build_args): | |||||||
|         if stack: |         if stack: | ||||||
|             print(f"Stack: {stack}") |             print(f"Stack: {stack}") | ||||||
| 
 | 
 | ||||||
|     # TODO: make this configurable |     container_build_env = make_container_build_env(dev_root_path, | ||||||
|     container_build_env = { |                                                    container_build_dir, | ||||||
|         "CERC_NPM_REGISTRY_URL": get_npm_registry_url(), |                                                    debug, | ||||||
|         "CERC_GO_AUTH_TOKEN": config("CERC_GO_AUTH_TOKEN", default=""), |                                                    force_rebuild, | ||||||
|         "CERC_NPM_AUTH_TOKEN": config("CERC_NPM_AUTH_TOKEN", default=""), |                                                    extra_build_args) | ||||||
|         "CERC_REPO_BASE_DIR": dev_root_path, |  | ||||||
|         "CERC_CONTAINER_BASE_DIR": container_build_dir, |  | ||||||
|         "CERC_HOST_UID": f"{os.getuid()}", |  | ||||||
|         "CERC_HOST_GID": f"{os.getgid()}", |  | ||||||
|         "DOCKER_BUILDKIT": config("DOCKER_BUILDKIT", default="0") |  | ||||||
|     } |  | ||||||
|     container_build_env.update({"CERC_SCRIPT_DEBUG": "true"} if debug else {}) |  | ||||||
|     container_build_env.update({"CERC_FORCE_REBUILD": "true"} if force_rebuild else {}) |  | ||||||
|     container_build_env.update({"CERC_CONTAINER_EXTRA_BUILD_ARGS": extra_build_args} if extra_build_args else {}) |  | ||||||
|     docker_host_env = os.getenv("DOCKER_HOST") |  | ||||||
|     if docker_host_env: |  | ||||||
|         container_build_env.update({"DOCKER_HOST": docker_host_env}) |  | ||||||
| 
 |  | ||||||
|     def process_container(container): |  | ||||||
|         if not quiet: |  | ||||||
|             print(f"Building: {container}") |  | ||||||
|         build_dir = os.path.join(container_build_dir, container.replace("/", "-")) |  | ||||||
|         build_script_filename = os.path.join(build_dir, "build.sh") |  | ||||||
|         if verbose: |  | ||||||
|             print(f"Build script filename: {build_script_filename}") |  | ||||||
|         if os.path.exists(build_script_filename): |  | ||||||
|             build_command = build_script_filename |  | ||||||
|         else: |  | ||||||
|             if verbose: |  | ||||||
|                 print(f"No script file found: {build_script_filename}, using default build script") |  | ||||||
|             repo_dir = container.split('/')[1] |  | ||||||
|             # TODO: make this less of a hack -- should be specified in some metadata somewhere |  | ||||||
|             # Check if we have a repo for this container. If not, set the context dir to the container-build subdir |  | ||||||
|             repo_full_path = os.path.join(dev_root_path, repo_dir) |  | ||||||
|             repo_dir_or_build_dir = repo_full_path if os.path.exists(repo_full_path) else build_dir |  | ||||||
|             build_command = os.path.join(container_build_dir, "default-build.sh") + f" {container}:local {repo_dir_or_build_dir}" |  | ||||||
|         if not dry_run: |  | ||||||
|             if verbose: |  | ||||||
|                 print(f"Executing: {build_command} with environment: {container_build_env}") |  | ||||||
|             build_result = subprocess.run(build_command, shell=True, env=container_build_env) |  | ||||||
|             if verbose: |  | ||||||
|                 print(f"Return code is: {build_result.returncode}") |  | ||||||
|             if build_result.returncode != 0: |  | ||||||
|                 print(f"Error running build for {container}") |  | ||||||
|                 if not continue_on_error: |  | ||||||
|                     print("FATAL Error: container build failed and --continue-on-error not set, exiting") |  | ||||||
|                     sys.exit(1) |  | ||||||
|                 else: |  | ||||||
|                     print("****** Container Build Error, continuing because --continue-on-error is set") |  | ||||||
|         else: |  | ||||||
|             print("Skipped") |  | ||||||
| 
 | 
 | ||||||
|     for container in containers_in_scope: |     for container in containers_in_scope: | ||||||
|         if include_exclude_check(container, include, exclude): |         if include_exclude_check(container, include, exclude): | ||||||
|             process_container(container) |             process_container(container, container_build_dir, container_build_env, | ||||||
|  |                               dev_root_path, quiet, verbose, dry_run, continue_on_error) | ||||||
|         else: |         else: | ||||||
|             if verbose: |             if verbose: | ||||||
|                 print(f"Excluding: {container}") |                 print(f"Excluding: {container}") | ||||||
|  | |||||||
							
								
								
									
										76
									
								
								stack_orchestrator/build/build_webapp.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								stack_orchestrator/build/build_webapp.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,76 @@ | |||||||
|  | # 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 <http:#www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | # 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 | ||||||
|  | from decouple import config | ||||||
|  | import click | ||||||
|  | from pathlib import Path | ||||||
|  | from stack_orchestrator.build import build_containers | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | @click.command() | ||||||
|  | @click.option('--base-container', default="cerc/nextjs-base") | ||||||
|  | @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.pass_context | ||||||
|  | def command(ctx, base_container, source_repo, force_rebuild, extra_build_args): | ||||||
|  |     '''build the specified webapp container''' | ||||||
|  | 
 | ||||||
|  |     quiet = ctx.obj.quiet | ||||||
|  |     verbose = ctx.obj.verbose | ||||||
|  |     dry_run = ctx.obj.dry_run | ||||||
|  |     debug = ctx.obj.debug | ||||||
|  |     local_stack = ctx.obj.local_stack | ||||||
|  |     stack = ctx.obj.stack | ||||||
|  |     continue_on_error = ctx.obj.continue_on_error | ||||||
|  | 
 | ||||||
|  |     # 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")] | ||||||
|  |         print(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 not quiet: | ||||||
|  |         print(f'Dev Root is: {dev_root_path}') | ||||||
|  | 
 | ||||||
|  |     # 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) | ||||||
|  | 
 | ||||||
|  |     build_containers.process_container(base_container, container_build_dir, container_build_env, dev_root_path, quiet, | ||||||
|  |                                        verbose, dry_run, continue_on_error) | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |     # Now build the target webapp.  We use the same build script, but with a different Dockerfile and work dir. | ||||||
|  |     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.webapp") | ||||||
|  |     webapp_name = os.path.abspath(source_repo).split(os.path.sep)[-1] | ||||||
|  |     container_build_env["CERC_CONTAINER_BUILD_TAG"] = f"cerc/{webapp_name}:local" | ||||||
|  | 
 | ||||||
|  |     build_containers.process_container(base_container, container_build_dir, container_build_env, dev_root_path, quiet, | ||||||
|  |                                        verbose, dry_run, continue_on_error) | ||||||
| @ -0,0 +1,55 @@ | |||||||
|  | # Originally from: https://github.com/devcontainers/images/blob/main/src/javascript-node/.devcontainer/Dockerfile | ||||||
|  | # [Choice] Node.js version (use -bullseye variants on local arm64/Apple Silicon): 18, 16, 14, 18-bullseye, 16-bullseye, 14-bullseye, 18-buster, 16-buster, 14-buster | ||||||
|  | ARG VARIANT=18-bullseye | ||||||
|  | FROM node:${VARIANT} | ||||||
|  | 
 | ||||||
|  | ARG USERNAME=node | ||||||
|  | ARG NPM_GLOBAL=/usr/local/share/npm-global | ||||||
|  | 
 | ||||||
|  | # Add NPM global to PATH. | ||||||
|  | ENV PATH=${NPM_GLOBAL}/bin:${PATH} | ||||||
|  | # Prevents npm from printing version warnings | ||||||
|  | ENV NPM_CONFIG_UPDATE_NOTIFIER=false | ||||||
|  | 
 | ||||||
|  | RUN \ | ||||||
|  |     # Configure global npm install location, use group to adapt to UID/GID changes | ||||||
|  |     if ! cat /etc/group | grep -e "^npm:" > /dev/null 2>&1; then groupadd -r npm; fi \ | ||||||
|  |     && usermod -a -G npm ${USERNAME} \ | ||||||
|  |     && umask 0002 \ | ||||||
|  |     && mkdir -p ${NPM_GLOBAL} \ | ||||||
|  |     && touch /usr/local/etc/npmrc \ | ||||||
|  |     && chown ${USERNAME}:npm ${NPM_GLOBAL} /usr/local/etc/npmrc \ | ||||||
|  |     && chmod g+s ${NPM_GLOBAL} \ | ||||||
|  |     && npm config -g set prefix ${NPM_GLOBAL} \ | ||||||
|  |     && su ${USERNAME} -c "npm config -g set prefix ${NPM_GLOBAL}" \ | ||||||
|  |     # Install eslint | ||||||
|  |     && su ${USERNAME} -c "umask 0002 && npm install -g eslint" \ | ||||||
|  |     && npm cache clean --force > /dev/null 2>&1 | ||||||
|  | 
 | ||||||
|  | # [Optional] Uncomment this section to install additional OS packages. | ||||||
|  | RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ | ||||||
|  |     && apt-get -y install --no-install-recommends jq gettext-base | ||||||
|  | 
 | ||||||
|  | # [Optional] Uncomment if you want to install an additional version of node using nvm | ||||||
|  | # ARG EXTRA_NODE_VERSION=10 | ||||||
|  | # RUN su node -c "source /usr/local/share/nvm/nvm.sh && nvm install ${EXTRA_NODE_VERSION}" | ||||||
|  | 
 | ||||||
|  | # We do this to get a yq binary from the published container, for the correct architecture we're building here | ||||||
|  | # COPY --from=docker.io/mikefarah/yq:latest /usr/bin/yq /usr/local/bin/yq | ||||||
|  | 
 | ||||||
|  | COPY /scripts /scripts | ||||||
|  | 
 | ||||||
|  | # [Optional] Uncomment if you want to install more global node modules | ||||||
|  | # RUN su node -c "npm install -g <your-package-list-here>" | ||||||
|  | 
 | ||||||
|  | # RUN mkdir -p /config | ||||||
|  | # COPY ./config.yml /config | ||||||
|  | 
 | ||||||
|  | # Install simple web server for now (use nginx perhaps later) | ||||||
|  | # RUN yarn global add http-server | ||||||
|  | 
 | ||||||
|  | # Expose port for http | ||||||
|  | EXPOSE 3000 | ||||||
|  | 
 | ||||||
|  | # Default command sleeps forever so docker doesn't kill it | ||||||
|  | CMD ["/scripts/start-serving-app.sh"] | ||||||
| @ -0,0 +1,5 @@ | |||||||
|  | FROM cerc/nextjs-base:local | ||||||
|  | WORKDIR /app | ||||||
|  | COPY . . | ||||||
|  | RUN rm -rf node_modules build .next* | ||||||
|  | RUN /scripts/build-app.sh /app | ||||||
							
								
								
									
										13
									
								
								stack_orchestrator/data/container-build/cerc-nextjs-base/build.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										13
									
								
								stack_orchestrator/data/container-build/cerc-nextjs-base/build.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,13 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  | # Build cerc/laconic-registry-cli | ||||||
|  | 
 | ||||||
|  | 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-base:local} | ||||||
|  | 
 | ||||||
|  | docker build -t $CERC_CONTAINER_BUILD_TAG ${build_command_args} -f $CERC_CONTAINER_BUILD_DOCKERFILE $CERC_CONTAINER_BUILD_WORK_DIR | ||||||
| @ -0,0 +1,36 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | 
 | ||||||
|  | if [ -n "$CERC_SCRIPT_DEBUG" ]; then | ||||||
|  |     set -x | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | WORK_DIR="${1:-./}" | ||||||
|  | SRC_DIR="${2:-.next}" | ||||||
|  | TRG_DIR="${3:-.next-r}" | ||||||
|  | 
 | ||||||
|  | cd "${WORK_DIR}" || exit 1 | ||||||
|  | 
 | ||||||
|  | rm -rf "$TRG_DIR" | ||||||
|  | mkdir -p "$TRG_DIR" | ||||||
|  | cp -rp "$SRC_DIR" "$TRG_DIR/" | ||||||
|  | 
 | ||||||
|  | if [ -f ".env" ]; then | ||||||
|  |   TMP_ENV=`mktemp` | ||||||
|  |   declare -px > $TMP_ENV | ||||||
|  |   set -a | ||||||
|  |   source .env | ||||||
|  |   source $TMP_ENV | ||||||
|  |   set +a | ||||||
|  |   rm -f $TMP_ENV | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | for f in $(find "$TRG_DIR" -regex ".*.[tj]sx?$" -type f | grep -v 'node_modules'); do | ||||||
|  |   for e in $(cat "${f}" | tr -s '[:blank:]' '\n' | tr -s '[{},()]' '\n' | egrep -o '^"CERC_RUNTIME_ENV[^\"]+"$'); do | ||||||
|  |     orig_name=$(echo -n "${e}" | sed 's/"//g') | ||||||
|  |     cur_name=$(echo -n "${orig_name}" | sed 's/CERC_RUNTIME_ENV_//g') | ||||||
|  |     cur_val=$(echo -n "\$${cur_name}" | envsubst) | ||||||
|  |     esc_val=$(sed 's/[&/\]/\\&/g' <<< "$cur_val") | ||||||
|  |     echo "$cur_name=$cur_val" | ||||||
|  |     sed -i "s/$orig_name/$esc_val/g" $f | ||||||
|  |   done | ||||||
|  | done | ||||||
| @ -0,0 +1,63 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | 
 | ||||||
|  | if [ -n "$CERC_SCRIPT_DEBUG" ]; then | ||||||
|  |     set -x | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) | ||||||
|  | 
 | ||||||
|  | WORK_DIR="${1:-/app}" | ||||||
|  | 
 | ||||||
|  | cd "${WORK_DIR}" || exit 1 | ||||||
|  | 
 | ||||||
|  | cp next.config.js next.config.dist | ||||||
|  | 
 | ||||||
|  | npm i -g js-beautify | ||||||
|  | js-beautify next.config.dist > next.config.js | ||||||
|  | 
 | ||||||
|  | npm install | ||||||
|  | 
 | ||||||
|  | CONFIG_LINES=$(wc -l next.config.js | awk '{ print $1 }') | ||||||
|  | MOD_EXPORTS_LINE=$(grep -n 'module.exports' next.config.js | cut -d':' -f1) | ||||||
|  | 
 | ||||||
|  | head -$(( ${MOD_EXPORTS_LINE} - 1 )) next.config.js > next.config.js.1 | ||||||
|  | 
 | ||||||
|  | cat > next.config.js.2 <<EOF | ||||||
|  | const webpack = require('webpack'); | ||||||
|  | 
 | ||||||
|  | let envMap; | ||||||
|  | try { | ||||||
|  |   // .env-list.json provides us a list of identifiers which should be replaced at runtime. | ||||||
|  |   envMap = require('./.env-list.json').reduce((a, v) => { | ||||||
|  |     a[v] = \`"CERC_RUNTIME_ENV_\${v.split(/\./).pop()}"\`; | ||||||
|  |     return a; | ||||||
|  |   }, {}); | ||||||
|  | } catch { | ||||||
|  |   // If .env-list.json cannot be loaded, we are probably running in dev mode, so use process.env instead. | ||||||
|  |   envMap = Object.keys(process.env).reduce((a, v) => { | ||||||
|  |     if (v.startsWith('CERC_')) { | ||||||
|  |       a[\`process.env.\${v}\`] = JSON.stringify(process.env[v]); | ||||||
|  |     } | ||||||
|  |     return a; | ||||||
|  |   }, {}); | ||||||
|  | } | ||||||
|  | EOF | ||||||
|  | 
 | ||||||
|  | grep 'module.exports' next.config.js > next.config.js.3 | ||||||
|  | 
 | ||||||
|  | cat > next.config.js.4 <<EOF | ||||||
|  |   webpack: (config) => { | ||||||
|  |     config.plugins.push(new webpack.DefinePlugin(envMap)); | ||||||
|  |     return config; | ||||||
|  |   }, | ||||||
|  | EOF | ||||||
|  | 
 | ||||||
|  | tail -$(( ${CONFIG_LINES} - ${MOD_EXPORTS_LINE} + 1 )) next.config.js | grep -v 'process\.env\.' > next.config.js.5 | ||||||
|  | 
 | ||||||
|  | cat next.config.js.* | js-beautify > next.config.js | ||||||
|  | rm next.config.js.* | ||||||
|  | 
 | ||||||
|  | "${SCRIPT_DIR}/find-env.sh" "$(pwd)" > .env-list.json | ||||||
|  | 
 | ||||||
|  | npm run build | ||||||
|  | rm .env-list.json | ||||||
							
								
								
									
										24
									
								
								stack_orchestrator/data/container-build/cerc-nextjs-base/scripts/find-env.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										24
									
								
								stack_orchestrator/data/container-build/cerc-nextjs-base/scripts/find-env.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | #!/bin/bash | ||||||
|  | 
 | ||||||
|  | if [ -n "$CERC_SCRIPT_DEBUG" ]; then | ||||||
|  |     set -x | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | WORK_DIR="${1:-./}" | ||||||
|  | TMPF=$(mktemp) | ||||||
|  | 
 | ||||||
|  | cd "$WORK_DIR" || exit 1 | ||||||
|  | 
 | ||||||
|  | for d in $(find . -maxdepth 1 -type d | grep -v '\./\.' | grep '/' | cut -d'/' -f2); do | ||||||
|  |   egrep "/$d[/$]?" .gitignore >/dev/null 2>/dev/null | ||||||
|  |   if [ $? -eq 0 ]; then | ||||||
|  |     continue | ||||||
|  |   fi | ||||||
|  | 
 | ||||||
|  |   for f in $(find "$d" -regex ".*.[tj]sx?$" -type f); do | ||||||
|  |     cat "$f" | tr -s '[:blank:]' '\n' | tr -s '[{},()]' '\n' | egrep -o 'process.env.[A-Za-z0-9_]+' >> $TMPF | ||||||
|  |   done | ||||||
|  | done | ||||||
|  | 
 | ||||||
|  | cat $TMPF | sort -u | jq  --raw-input .  | jq --slurp . | ||||||
|  | rm -f $TMPF | ||||||
| @ -0,0 +1,13 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  | if [ -n "$CERC_SCRIPT_DEBUG" ]; then | ||||||
|  |     set -x | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) | ||||||
|  | 
 | ||||||
|  | CERC_WEBAPP_FILES_DIR="${CERC_WEBAPP_FILES_DIR:-/app}" | ||||||
|  | cd "$CERC_WEBAPP_FILES_DIR" | ||||||
|  | 
 | ||||||
|  | rm -rf .next-r | ||||||
|  | "$SCRIPT_DIR/apply-runtime-env.sh" "`pwd`" .next .next-r | ||||||
|  | npm start .next-r -p ${CERC_LISTEN_PORT:-3000} | ||||||
| @ -19,6 +19,7 @@ from stack_orchestrator.command_types import CommandOptions | |||||||
| from stack_orchestrator.repos import setup_repositories | from stack_orchestrator.repos import setup_repositories | ||||||
| from stack_orchestrator.build import build_containers | from stack_orchestrator.build import build_containers | ||||||
| from stack_orchestrator.build import build_npms | from stack_orchestrator.build import build_npms | ||||||
|  | from stack_orchestrator.build import build_webapp | ||||||
| from stack_orchestrator.deploy import deploy | from stack_orchestrator.deploy import deploy | ||||||
| from stack_orchestrator import version | from stack_orchestrator import version | ||||||
| from stack_orchestrator.deploy import deployment | from stack_orchestrator.deploy import deployment | ||||||
| @ -48,6 +49,7 @@ def cli(ctx, stack, quiet, verbose, dry_run, local_stack, debug, continue_on_err | |||||||
| cli.add_command(setup_repositories.command, "setup-repositories") | cli.add_command(setup_repositories.command, "setup-repositories") | ||||||
| cli.add_command(build_containers.command, "build-containers") | cli.add_command(build_containers.command, "build-containers") | ||||||
| cli.add_command(build_npms.command, "build-npms") | cli.add_command(build_npms.command, "build-npms") | ||||||
|  | cli.add_command(build_webapp.command, "build-webapp") | ||||||
| cli.add_command(deploy.command, "deploy")  # deploy is an alias for deploy-system | cli.add_command(deploy.command, "deploy")  # deploy is an alias for deploy-system | ||||||
| cli.add_command(deploy.command, "deploy-system") | cli.add_command(deploy.command, "deploy-system") | ||||||
| cli.add_command(deployment.command, "deployment") | cli.add_command(deployment.command, "deployment") | ||||||
|  | |||||||
							
								
								
									
										62
									
								
								tests/webapp-test/run-webapp-test.sh
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										62
									
								
								tests/webapp-test/run-webapp-test.sh
									
									
									
									
									
										Executable file
									
								
							| @ -0,0 +1,62 @@ | |||||||
|  | #!/usr/bin/env bash | ||||||
|  | set -e | ||||||
|  | if [ -n "$CERC_SCRIPT_DEBUG" ]; then | ||||||
|  |   set -x | ||||||
|  | fi | ||||||
|  | # Dump environment variables for debugging | ||||||
|  | echo "Environment variables:" | ||||||
|  | env | ||||||
|  | # Test basic stack-orchestrator webapp | ||||||
|  | echo "Running stack-orchestrator webapp test" | ||||||
|  | # Bit of a hack, test the most recent package | ||||||
|  | TEST_TARGET_SO=$( ls -t1 ./package/laconic-so* | head -1 ) | ||||||
|  | # Set a non-default repo dir | ||||||
|  | export CERC_REPO_BASE_DIR=~/stack-orchestrator-test/repo-base-dir | ||||||
|  | echo "Testing this package: $TEST_TARGET_SO" | ||||||
|  | echo "Test version command" | ||||||
|  | reported_version_string=$( $TEST_TARGET_SO version ) | ||||||
|  | echo "Version reported is: ${reported_version_string}" | ||||||
|  | echo "Cloning repositories into: $CERC_REPO_BASE_DIR" | ||||||
|  | rm -rf $CERC_REPO_BASE_DIR | ||||||
|  | mkdir -p $CERC_REPO_BASE_DIR | ||||||
|  | git clone https://git.vdb.to/cerc-io/test-progressive-web-app.git $CERC_REPO_BASE_DIR/test-progressive-web-app | ||||||
|  | 
 | ||||||
|  | # Test webapp command execution | ||||||
|  | $TEST_TARGET_SO build-webapp --source-repo $CERC_REPO_BASE_DIR/test-progressive-web-app | ||||||
|  | 
 | ||||||
|  | UUID=`uuidgen` | ||||||
|  | 
 | ||||||
|  | set +e | ||||||
|  | 
 | ||||||
|  | CONTAINER_ID=$(docker run -p 3000:3000 -d cerc/test-progressive-web-app:local) | ||||||
|  | sleep 3 | ||||||
|  | wget -O test.before -m http://localhost:3000 | ||||||
|  | 
 | ||||||
|  | docker remove -f $CONTAINER_ID | ||||||
|  | 
 | ||||||
|  | CONTAINER_ID=$(docker run -p 3000:3000 -e CERC_WEBAPP_DEBUG=$UUID -d cerc/test-progressive-web-app:local) | ||||||
|  | sleep 3 | ||||||
|  | wget -O test.after -m http://localhost:3000 | ||||||
|  | 
 | ||||||
|  | docker remove -f $CONTAINER_ID | ||||||
|  | 
 | ||||||
|  | echo "###########################################################################" | ||||||
|  | echo "" | ||||||
|  | 
 | ||||||
|  | grep "$UUID" test.before > /dev/null | ||||||
|  | if [ $? -ne 1 ]; then | ||||||
|  |   echo "BEFORE: FAILED" | ||||||
|  |   exit 1 | ||||||
|  | else | ||||||
|  |   echo "BEFORE: PASSED" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | grep "`uuidgen`" test.after > /dev/null | ||||||
|  | if [ $? -ne 0 ]; then | ||||||
|  |   echo "AFTER: FAILED" | ||||||
|  |   exit 1 | ||||||
|  | else | ||||||
|  |   echo "AFTER: PASSED" | ||||||
|  | fi | ||||||
|  | 
 | ||||||
|  | exit 0 | ||||||
							
								
								
									
										34
									
								
								tests/webapp-test/test.before
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								tests/webapp-test/test.before
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Loading…
	
		Reference in New Issue
	
	Block a user