From 2c511f1bbb9ac58aa3a6e2b8cfa088307aed5358 Mon Sep 17 00:00:00 2001 From: Theo Ilie Date: Thu, 21 Dec 2023 17:02:37 -0800 Subject: [PATCH] Build protocol dashboard ~9x smaller in CI instead of at runtime --- dev-tools/audius-compose | 48 +++++++++++++++---- ...docker-compose.protocol-dashboard.dev.yml} | 0 ...docker-compose.protocol-dashboard.prod.yml | 12 +++++ ...ocker-compose.protocol-dashboard.stage.yml | 12 +++++ dev-tools/compose/docker-compose.yml | 2 +- protocol-dashboard/Dockerfile.fast | 14 ++++++ protocol-dashboard/Dockerfile.prod | 29 +++++++++++ protocol-dashboard/Dockerfile.stage | 29 +++++++++++ .../scripts/docker-entrypoint.sh | 35 +++++++++----- 9 files changed, 157 insertions(+), 24 deletions(-) rename dev-tools/compose/{docker-compose.protocol-dashboard.yml => docker-compose.protocol-dashboard.dev.yml} (100%) create mode 100644 dev-tools/compose/docker-compose.protocol-dashboard.prod.yml create mode 100644 dev-tools/compose/docker-compose.protocol-dashboard.stage.yml create mode 100644 protocol-dashboard/Dockerfile.fast create mode 100644 protocol-dashboard/Dockerfile.prod create mode 100644 protocol-dashboard/Dockerfile.stage diff --git a/dev-tools/audius-compose b/dev-tools/audius-compose index a4cfe30c441..866107087ad 100755 --- a/dev-tools/audius-compose +++ b/dev-tools/audius-compose @@ -213,6 +213,8 @@ def build( protocol_dir, discovery_provider_replicas, elasticsearch_replicas, prod ) + env = os.environ.copy() + env["DASHBOARD_ENV_TYPE"] = "prod" if prod else "dev" proc = subprocess.run( [ "docker", @@ -241,11 +243,33 @@ def build( *args, *services, ], + env=env, ) if proc.returncode != 0: raise click.ClickException("Failed to build images") + # build an extra image for protocol dashboard on staging + if "dashboard" in services and prod: + env = os.environ.copy() + env["DASHBOARD_ENV_TYPE"] = "stage" + proc = subprocess.run( + [ + "docker", + "compose", + f"--project-directory={protocol_dir / 'dev-tools/compose'}", + f"--file={protocol_dir / 'dev-tools/compose/docker-compose.yml'}", + f"--project-name={protocol_dir.name}", + "build", + *args, + "dashboard", + ], + env=env, + ) + + if proc.returncode != 0: + raise click.ClickException("Failed to build images") + @cli.command() @click.argument("services", nargs=-1) @@ -269,16 +293,20 @@ def push(ctx, services, prod): try: for service in services: - # TODO: Find if there is a better way to find source image names - subprocess.run( - [ - "docker", - "tag", - f"{protocol_dir.name}-{service}", - f"audius/{service}:{git_commit}", - ], - check=True, - ) + git_commit = get_git_commit(protocol_dir) + base_tag = f"{protocol_dir.name}-{service}" + + if service == "dashboard" and prod: + # tag and push -stage image + subprocess.run(["docker", "tag", base_tag, f"audius/{service}:{git_commit}-stage"], check=True) + subprocess.run(["docker", "push", f"audius/{service}:{git_commit}-stage"], check=True) + + # tag and push -prod image + subprocess.run(["docker", "tag", base_tag, f"audius/{service}:{git_commit}-prod"], check=True) + subprocess.run(["docker", "push", f"audius/{service}:{git_commit}-prod"], check=True) + else: + subprocess.run(["docker", "tag", base_tag, f"audius/{service}:{git_commit}"], check=True) + subprocess.run(["docker", "push", f"audius/{service}:{git_commit}"], check=True) except subprocess.CalledProcessError: raise click.ClickException("Failed to tag images") diff --git a/dev-tools/compose/docker-compose.protocol-dashboard.yml b/dev-tools/compose/docker-compose.protocol-dashboard.dev.yml similarity index 100% rename from dev-tools/compose/docker-compose.protocol-dashboard.yml rename to dev-tools/compose/docker-compose.protocol-dashboard.dev.yml diff --git a/dev-tools/compose/docker-compose.protocol-dashboard.prod.yml b/dev-tools/compose/docker-compose.protocol-dashboard.prod.yml new file mode 100644 index 00000000000..6e2735c2b12 --- /dev/null +++ b/dev-tools/compose/docker-compose.protocol-dashboard.prod.yml @@ -0,0 +1,12 @@ +version: "3.9" + +services: + dashboard: + container_name: dashboard + build: + context: ${PROJECT_ROOT}/protocol-dashboard + dockerfile: ${PROJECT_ROOT}/protocol-dashboard/Dockerfile.prod + ports: + - 5173:5173 + deploy: + mode: global diff --git a/dev-tools/compose/docker-compose.protocol-dashboard.stage.yml b/dev-tools/compose/docker-compose.protocol-dashboard.stage.yml new file mode 100644 index 00000000000..b2abe710cdc --- /dev/null +++ b/dev-tools/compose/docker-compose.protocol-dashboard.stage.yml @@ -0,0 +1,12 @@ +version: "3.9" + +services: + dashboard: + container_name: dashboard + build: + context: ${PROJECT_ROOT}/protocol-dashboard + dockerfile: ${PROJECT_ROOT}/protocol-dashboard/Dockerfile.stage + ports: + - 5173:5173 + deploy: + mode: global diff --git a/dev-tools/compose/docker-compose.yml b/dev-tools/compose/docker-compose.yml index 1363a6f1d28..bd5dfa186b8 100644 --- a/dev-tools/compose/docker-compose.yml +++ b/dev-tools/compose/docker-compose.yml @@ -91,7 +91,7 @@ services: dashboard: extends: - file: docker-compose.protocol-dashboard.yml + file: docker-compose.protocol-dashboard.${DASHBOARD_ENV_TYPE:-dev}.yml service: dashboard <<: *common diff --git a/protocol-dashboard/Dockerfile.fast b/protocol-dashboard/Dockerfile.fast new file mode 100644 index 00000000000..9770707e113 --- /dev/null +++ b/protocol-dashboard/Dockerfile.fast @@ -0,0 +1,14 @@ +# To use this file for testing on staging: +# 0. npm i && npm run build:stage +# 1. in docker-compose.protocol-dashboard.stage.yml, set dockerfile to Dockerfile.fast +# 2. comment out dist/ in .dockerignore +# 3. run audius-compose push --prod "dashboard" (comment out in audius-compose build where it builds for prod, and set env["DOCKER_DEFAULT_PLATFORM"] = "linux/amd64") + +FROM alpine:latest +WORKDIR /app +RUN apk add --no-cache findutils coreutils +COPY dist /tmp/dist + +COPY ./scripts/docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] \ No newline at end of file diff --git a/protocol-dashboard/Dockerfile.prod b/protocol-dashboard/Dockerfile.prod new file mode 100644 index 00000000000..0a0a3c37c31 --- /dev/null +++ b/protocol-dashboard/Dockerfile.prod @@ -0,0 +1,29 @@ +FROM node:18.17-slim as builder +RUN apt update && \ + apt install -y \ + python3 \ + make \ + gcc \ + g++ \ + libusb-1.0-0-dev \ + libudev-dev \ + libsecret-1-dev \ + pkg-config && \ + rm -rf /var/lib/apt/lists/* +WORKDIR /app +COPY package*.json ./ +ENV DASHBOARD_BASE_URL="/dashboard/" +RUN npm install --legacy-peer-deps +COPY .env* index.html tsconfig.json tsconfig.node.json vite.config.ts ./ +COPY src ./src +COPY public ./public +COPY types ./types +RUN npm run build:prod + +FROM alpine:latest +WORKDIR /app +RUN apk add --no-cache findutils coreutils +COPY --from=builder /app/dist /tmp/dist +COPY ./scripts/docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] diff --git a/protocol-dashboard/Dockerfile.stage b/protocol-dashboard/Dockerfile.stage new file mode 100644 index 00000000000..35307461630 --- /dev/null +++ b/protocol-dashboard/Dockerfile.stage @@ -0,0 +1,29 @@ +FROM node:18.17-slim as builder +RUN apt update && \ + apt install -y \ + python3 \ + make \ + gcc \ + g++ \ + libusb-1.0-0-dev \ + libudev-dev \ + libsecret-1-dev \ + pkg-config && \ + rm -rf /var/lib/apt/lists/* +WORKDIR /app +COPY package*.json ./ +ENV DASHBOARD_BASE_URL="/dashboard/" +RUN npm install --legacy-peer-deps +COPY .env* index.html tsconfig.json tsconfig.node.json vite.config.ts ./ +COPY src ./src +COPY public ./public +COPY types ./types +RUN npm run build:stage + +FROM alpine:latest +WORKDIR /app +RUN apk add --no-cache findutils coreutils +COPY --from=builder /app/dist /tmp/dist +COPY ./scripts/docker-entrypoint.sh /usr/local/bin/ +RUN chmod +x /usr/local/bin/docker-entrypoint.sh +ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"] diff --git a/protocol-dashboard/scripts/docker-entrypoint.sh b/protocol-dashboard/scripts/docker-entrypoint.sh index 8e215578908..fcbbef654d1 100644 --- a/protocol-dashboard/scripts/docker-entrypoint.sh +++ b/protocol-dashboard/scripts/docker-entrypoint.sh @@ -1,20 +1,29 @@ #!/bin/sh -FIRST_RUN_FLAG="./.firstrun" +# Directory where build artifacts are stored in the image before being copied to the volume +TEMP_BUILD_DIR="/tmp/dist" -if [ ! -f "$FIRST_RUN_FLAG" ]; then - echo "Building dist..." - npm run build:$NETWORK +# Directory mounted as a volume +VOLUME_DIR="/app/dist" - if [ $? -eq 0 ]; then - echo "Successfully built dist" - touch "$FIRST_RUN_FLAG" - else - echo "'npm run build:$NETWORK' failed with exit code $?. Exiting..." - exit 1 - fi +# Calculate the current checksum of the temporary build directory +CHECKSUM_FILE="$VOLUME_DIR/.checksum" +current_checksum=$(find "$TEMP_BUILD_DIR" -type f -exec md5sum {} \; | md5sum | cut -d' ' -f1) + +# Read the last checksum (if it exists) +last_checksum="" +if [ -f "$CHECKSUM_FILE" ]; then + last_checksum=$(cat "$CHECKSUM_FILE") +fi + +# Compare checksums and copy if different +if [ "$current_checksum" != "$last_checksum" ]; then + echo "Changes detected. Updating files in volume..." + cp -r $TEMP_BUILD_DIR/* $VOLUME_DIR/ + echo "$current_checksum" > "$CHECKSUM_FILE" else - echo "dist already built" + echo "No changes detected. No update needed." fi -exec npm run serve +# Keep the container running by tailing /dev/null +tail -f /dev/null