From 13d2e484e6590e57de93ab4327410ff8d25233a0 Mon Sep 17 00:00:00 2001 From: Dennis Felsing Date: Wed, 25 Feb 2026 12:07:59 +0000 Subject: [PATCH 1/3] console: Enable e2e tests, previews, deploys and fix readmes --- .github/CODEOWNERS | 1 + bin/ci-builder | 16 +- ci/builder/Dockerfile | 159 +++++++++++++- ci/deploy/console.sh | 24 +++ ci/deploy/pipeline.template.yml | 10 + ci/mkpipeline.py | 19 ++ ci/mkpipeline.sh | 2 +- ci/test/console/cleanup-regions.mjs | 62 ++++++ ci/test/console/console-e2e.sh | 154 -------------- .../{console-e2e-prod.sh => e2e-prod.sh} | 33 ++- ci/test/console/e2e.sh | 194 ++++++++++++++++++ ci/test/console/embed-test-results.sh | 60 ++++++ ci/test/console/{console-lint.sh => lint.sh} | 0 ci/test/console/{console-test.sh => test.sh} | 0 ci/test/console/vercel-preview.sh | 28 +++ ci/test/pipeline.template.yml | 53 +++-- console/CLAUDE.md | 5 +- console/README.md | 23 ++- console/bin/apply-vercel-csp.js | 0 console/bin/build-ci | 0 console/bin/check-trufflehog-logs | 0 console/bin/check-trufflehog-repo | 0 console/bin/generate-types | 0 console/bin/latest-cloud-docker.js | 4 +- console/bin/latest-materialize-docker.js | 0 console/bin/vercel-preview-url.js | 0 console/doc/CI.md | 2 +- console/doc/design/20241004_freshness.md | 14 +- console/doc/design/README.md | 2 +- console/doc/guide-testing.md | 17 +- console/e2e-tests/platform.spec.ts | 7 +- console/misc/docker/README.md | 38 +--- console/package.json | 4 +- console/redocly.yaml | 6 +- 34 files changed, 675 insertions(+), 262 deletions(-) create mode 100755 ci/deploy/console.sh create mode 100755 ci/test/console/cleanup-regions.mjs delete mode 100755 ci/test/console/console-e2e.sh rename ci/test/console/{console-e2e-prod.sh => e2e-prod.sh} (59%) create mode 100755 ci/test/console/e2e.sh create mode 100755 ci/test/console/embed-test-results.sh rename ci/test/console/{console-lint.sh => lint.sh} (100%) rename ci/test/console/{console-test.sh => test.sh} (100%) create mode 100755 ci/test/console/vercel-preview.sh mode change 100644 => 100755 console/bin/apply-vercel-csp.js mode change 100644 => 100755 console/bin/build-ci mode change 100644 => 100755 console/bin/check-trufflehog-logs mode change 100644 => 100755 console/bin/check-trufflehog-repo mode change 100644 => 100755 console/bin/generate-types mode change 100644 => 100755 console/bin/latest-cloud-docker.js mode change 100644 => 100755 console/bin/latest-materialize-docker.js mode change 100644 => 100755 console/bin/vercel-preview-url.js diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 2d6dab9d33654..5b8129611c5bb 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -15,6 +15,7 @@ /bin/lint-versions @MaterializeInc/testing /ci @MaterializeInc/testing /ci/test/lint-deps.toml +/console @MaterializeInc/console /doc/user @MaterializeInc/docs /doc/developer/reference/compute @MaterializeInc/cluster /doc/developer/reference/storage @MaterializeInc/cluster diff --git a/bin/ci-builder b/bin/ci-builder index cdeec9ac79f92..3e39ca674fa45 100755 --- a/bin/ci-builder +++ b/bin/ci-builder @@ -25,7 +25,7 @@ cd "$(dirname "$0")/.." if [[ $# -lt 2 ]] then - echo "usage: $0 [...] + echo "usage: $0 [...] Manages the ci-builder Docker image, which contains the dependencies required to build, test, and deploy the code in this repository. @@ -59,6 +59,10 @@ case "$flavor" in rust_version=nightly rust_date=/$NIGHTLY_RUST_DATE ;; + console) + docker_target=ci-builder-console + rust_version=$(sed -n 's/rust-version = "\(.*\)"/\1/p' Cargo.toml) + ;; *) printf "unknown CI builder flavor %q\n" "$flavor" exit 1 @@ -310,6 +314,16 @@ case "$cmd" in --env DOCKERHUB_ACCESS_TOKEN # For configuring the metadata store --env EXTERNAL_METADATA_STORE + # For console + --env CONSOLE_E2E_TEST_STAGING_PASSWORD + --env CONSOLE_E2E_TEST_PRODUCTION_PASSWORD + --env E2E_TEST_STAGING_FRONTEGG_CLIENT_ID + --env E2E_TEST_STAGING_ORB_API_KEY + --env E2E_TEST_LICENSE_KEY + --env CLOUD_DEPLOY_KEY + --env VERCEL_TOKEN + --env VERCEL_ORG_ID + --env VERCEL_PROJECT_ID ) for env in $(printenv | grep -E '^(BUILDKITE|MZCOMPOSE|CI)' | sed 's/=.*//'); do args+=(--env "$env") diff --git a/ci/builder/Dockerfile b/ci/builder/Dockerfile index 568bd4a02cac5..f6db39761d223 100644 --- a/ci/builder/Dockerfile +++ b/ci/builder/Dockerfile @@ -44,7 +44,6 @@ RUN apt-get update --fix-missing && TZ=UTC DEBIAN_FRONTEND=noninteractive apt-ge gdb \ git \ gnupg2 \ - libxml2-16 \ pigz \ python3 \ python3.13-venv \ @@ -146,6 +145,7 @@ RUN apt-get update --fix-missing && TZ=UTC DEBIAN_FRONTEND=noninteractive apt-ge libclang-dev \ libclang-rt-18-dev \ libpq-dev \ + libxml2-16 \ lld \ llvm \ make \ @@ -172,7 +172,7 @@ COPY nodesource.asc . RUN gpg --dearmor < nodesource.asc > /etc/apt/keyrings/nodesource.gpg \ && rm nodesource.asc \ - && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_25.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ && apt-get update \ && apt-get install -y --no-install-recommends nodejs \ && apt-get clean \ @@ -399,3 +399,158 @@ ENV HELM_PLUGINS=/usr/local/share/helm/plugins ENV CARGO_HOME=/cargo RUN mkdir /cargo && chmod 777 /cargo VOLUME /cargo + +# Stage 3: Build a lightweight CI Builder image for console/playwright jobs. +FROM ubuntu:noble-20250529 AS ci-builder-console + +ARG ARCH_GCC +ARG ARCH_GO + +WORKDIR /workdir + +ENV RUST_BACKTRACE=1 +ENV PYTHONUNBUFFERED=1 +ENV MZ_DEV_CI_BUILDER=1 +ENV DOCKER_BUILDKIT=1 + +ARG XZ_OPT=-T0 + +RUN apt-get update --fix-missing && TZ=UTC DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + ca-certificates \ + curl \ + dnsmasq \ + docker.io \ + eatmydata \ + gettext-base \ + git \ + gnupg2 \ + jq \ + libatomic1 \ + openssh-client \ + perl \ + python3 \ + python3-venv \ + python-is-python3 \ + sudo \ + libasound2t64 \ + libatk1.0-0t64 \ + libatk-bridge2.0-0t64 \ + libatspi2.0-0t64 \ + libcairo2 \ + libcups2t64 \ + libdbus-1-3 \ + libdrm2 \ + libgbm1 \ + libnspr4 \ + libnss3 \ + libpango-1.0-0 \ + libx11-6 \ + libxcb1 \ + libxcomposite1 \ + libxdamage1 \ + libxext6 \ + libxfixes3 \ + libxkbcommon0 \ + libxrandr2 \ + gstreamer1.0-libav \ + libcairo-gobject2 \ + libenchant-2-2 \ + libepoxy0 \ + libevent-2.1-7t64 \ + libflite1 \ + libgdk-pixbuf-2.0-0 \ + libgles2 \ + libgstreamer1.0-0 \ + libgstreamer-gl1.0-0 \ + libgstreamer-plugins-bad1.0-0 \ + libgstreamer-plugins-base1.0-0 \ + libgtk-3-0t64 \ + libharfbuzz-icu0 \ + libhyphen0 \ + libicu74 \ + libjpeg-turbo8 \ + liblcms2-2 \ + libmanette-0.2-0 \ + libopus0 \ + libpangocairo-1.0-0 \ + libsecret-1-0 \ + libvpx9 \ + libwebp7 \ + libwebpdemux2 \ + libwoff1 \ + libxml2 \ + libxslt1.1 \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /usr/share/doc/* /usr/share/man/* /usr/share/info/* /usr/share/locale/* /var/cache/* /var/log/* \ + && sed -i 's/^UID_MIN.*/UID_MIN\t500/' /etc/login.defs + +RUN curl -fsSL https://github.com/MaterializeInc/autouseradd/releases/download/1.3.0/autouseradd-1.3.0-$ARCH_GO.tar.gz \ + | tar xz -C / --strip-components 1 + +RUN mkdir -p /usr/local/lib/docker/cli-plugins \ + && curl -fsSL https://github.com/docker/compose/releases/download/v5.0.1/docker-compose-linux-$ARCH_GCC > /usr/local/lib/docker/cli-plugins/docker-compose \ + && chmod +x /usr/local/lib/docker/cli-plugins/docker-compose \ + && curl -fsSL https://github.com/docker/buildx/releases/download/v0.30.1/buildx-v0.30.1.linux-$ARCH_GO > /usr/local/lib/docker/cli-plugins/docker-buildx \ + && chmod +x /usr/local/lib/docker/cli-plugins/docker-buildx + +COPY nodesource.asc . + +RUN gpg --dearmor < nodesource.asc > /etc/apt/keyrings/nodesource.gpg \ + && rm nodesource.asc \ + && echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" > /etc/apt/sources.list.d/nodesource.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends nodejs \ + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* \ + && rm -rf /usr/share/doc/nodejs \ + && npm install -g corepack \ + && corepack enable + +# TODO(def-) Upgrading kind/kubectl seems to cause Cloudtest failures +RUN curl -fsSL https://kind.sigs.k8s.io/dl/v0.29.0/kind-linux-$ARCH_GO > /usr/local/bin/kind \ + && chmod +x /usr/local/bin/kind \ + && if [ $ARCH_GO = amd64 ]; then echo 'c72eda46430f065fb45c5f70e7c957cc9209402ef309294821978677c8fb3284 /usr/local/bin/kind' | sha256sum --check; fi \ + && if [ $ARCH_GO = arm64 ]; then echo '03d45095dbd9cc1689f179a3e5e5da24b77c2d1b257d7645abf1b4174bebcf2a /usr/local/bin/kind' | sha256sum --check; fi + +RUN curl -fsSL https://dl.k8s.io/release/v1.24.3/bin/linux/$ARCH_GO/kubectl > /usr/local/bin/kubectl \ + && chmod +x /usr/local/bin/kubectl \ + && if [ $ARCH_GO = amd64 ]; then echo '8a45348bdaf81d46caf1706c8bf95b3f431150554f47d444ffde89e8cdd712c1 /usr/local/bin/kubectl' | sha256sum --check; fi \ + && if [ $ARCH_GO = arm64 ]; then echo 'bdad4d3063ddb7bfa5ecf17fb8b029d5d81d7d4ea1650e4369aafa13ed97149a /usr/local/bin/kubectl' | sha256sum --check; fi + +# Use Helm 3 (not 4) because the cloud repo's bin/kind-create uses relative +# file paths for vendored charts, which Helm 4 rejects as invalid URLs. +RUN curl -fsSL https://get.helm.sh/helm-v3.16.2-linux-$ARCH_GO.tar.gz > helm.tar.gz \ + && if [ $ARCH_GO = amd64 ]; then echo '9318379b847e333460d33d291d4c088156299a26cd93d570a7f5d0c36e50b5bb helm.tar.gz' | sha256sum --check; fi \ + && if [ $ARCH_GO = arm64 ]; then echo '1888301aeb7d08a03b6d9f4d2b73dcd09b89c41577e80e3455c113629fc657a4 helm.tar.gz' | sha256sum --check; fi \ + && tar -xf helm.tar.gz -C /usr/local/bin --strip-components=1 linux-$ARCH_GO/helm \ + && rm helm.tar.gz + +# Install just cargo (no rustc) — cloud scripts use it to read crate metadata. +COPY rust.asc . + +ARG RUST_DATE +ARG RUST_VERSION + +RUN gpg --import rust.asc \ + && rm rust.asc \ + && echo "trusted-key 85AB96E6FA1BE5FE" >> ~/.gnupg/gpg.conf \ + && mkdir rust \ + && curl -fsSL https://static.rust-lang.org/dist$RUST_DATE/rust-$RUST_VERSION-$ARCH_GCC-unknown-linux-gnu.tar.gz > rust.tar.gz \ + && curl -fsSL https://static.rust-lang.org/dist$RUST_DATE/rust-$RUST_VERSION-$ARCH_GCC-unknown-linux-gnu.tar.gz.asc > rust.asc \ + && gpg --verify rust.asc rust.tar.gz \ + && tar -xzf rust.tar.gz -C rust --strip-components=1 \ + && rust/install.sh --components=cargo \ + && rm -rf rust.asc rust.tar.gz rust + +COPY ssh_known_hosts /etc/ssh/ssh_known_hosts + +# Remove Ubuntu user causing UID collisions. +# https://bugs.launchpad.net/cloud-images/+bug/2005129 +RUN userdel -r ubuntu + +# Allow passwordless sudo for the CI user (created at runtime by autouseradd). +RUN echo "ALL ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/ci \ + && chmod 0440 /etc/sudoers.d/ci + +ENTRYPOINT ["autouseradd", "--user", "materialize"] diff --git a/ci/deploy/console.sh b/ci/deploy/console.sh new file mode 100755 index 0000000000000..f0a2b06d90942 --- /dev/null +++ b/ci/deploy/console.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +# Copyright Materialize, Inc. and contributors. All rights reserved. +# +# Use of this software is governed by the Business Source License +# included in the LICENSE file at the root of this repository. +# +# As of the Change Date specified in that file, in accordance with +# the Business Source License, use of this software will be governed +# by the Apache License, Version 2.0. + +set -euo pipefail + +cd console +export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +export VERCEL_ENVIRONMENT=production +export SENTRY_RELEASE="$BUILDKITE_COMMIT" +corepack enable +yarn install --immutable --network-timeout 30000 + +npx vercel@latest pull --yes --environment="$VERCEL_ENVIRONMENT" --token="$VERCEL_TOKEN" +npx vercel@latest build --token="$VERCEL_TOKEN" --prod + +npx vercel@latest deploy --prebuilt --prod --token="$VERCEL_TOKEN" diff --git a/ci/deploy/pipeline.template.yml b/ci/deploy/pipeline.template.yml index c2d78517d3610..344930fea399c 100644 --- a/ci/deploy/pipeline.template.yml +++ b/ci/deploy/pipeline.template.yml @@ -47,3 +47,13 @@ steps: queue: linux-x86_64-small concurrency: 1 concurrency_group: deploy/npm + + - id: deploy-console + label: ":rocket: :vercel: Console" + command: bin/ci-builder run stable ci/deploy/console.sh + branches: main + timeout_in_minutes: 15 + agents: + queue: linux-x86_64-small + concurrency: 1 + concurrency_group: deploy/console diff --git a/ci/mkpipeline.py b/ci/mkpipeline.py index d9ac413e8cebf..102dfe25e9801 100644 --- a/ci/mkpipeline.py +++ b/ci/mkpipeline.py @@ -175,6 +175,10 @@ def fetch_hashes() -> None: args.sanitizer, lto, ) + # Steps marked ci_glue_exempt should still be trimmed based on + # their own inputs, even when CI glue code has changed, since + # they don't depend on CI infrastructure (e.g. console e2e tests). + trim_ci_glue_exempt_steps(pipeline) else: print("Trimming unchanged steps from pipeline") trim_tests_pipeline( @@ -1036,6 +1040,19 @@ def have_paths_changed(globs: Iterable[str]) -> bool: raise RuntimeError("unreachable") +def trim_ci_glue_exempt_steps(pipeline: Any) -> None: + """Trim steps marked with ci_glue_exempt based on their own inputs, even + when CI glue code has changed. These steps (e.g. console e2e tests) don't + depend on CI infrastructure and should only run when their declared inputs + have actually changed.""" + for step in steps(pipeline): + if not step.get("ci_glue_exempt"): + continue + inputs = step.get("inputs", []) + if inputs and not have_paths_changed(inputs): + step["skip"] = "No changes in inputs" + + def remove_mz_specific_keys(pipeline: Any) -> None: """Remove the Materialize-specific keys from the configuration that are only used to inform how to trim the pipeline and for coverage runs.""" for step in steps(pipeline): @@ -1045,6 +1062,8 @@ def remove_mz_specific_keys(pipeline: Any) -> None: del step["coverage"] if "sanitizer" in step: del step["sanitizer"] + if "ci_glue_exempt" in step: + del step["ci_glue_exempt"] if ( "timeout_in_minutes" not in step and "prompt" not in step diff --git a/ci/mkpipeline.sh b/ci/mkpipeline.sh index 0990e6b28dbd9..d0ce13f3c4e23 100755 --- a/ci/mkpipeline.sh +++ b/ci/mkpipeline.sh @@ -27,7 +27,7 @@ bootstrap_steps= tmpfile=$(mktemp) for arch in x86_64 aarch64; do - for flavor in stable nightly min; do + for flavor in stable nightly min console; do ( if ! MZ_DEV_CI_BUILDER_ARCH=$arch bin/ci-builder exists $flavor; then echo "$arch:$flavor" >> "$tmpfile" diff --git a/ci/test/console/cleanup-regions.mjs b/ci/test/console/cleanup-regions.mjs new file mode 100755 index 0000000000000..00c9cacfd757a --- /dev/null +++ b/ci/test/console/cleanup-regions.mjs @@ -0,0 +1,62 @@ +#!/usr/bin/env node + +// Copyright Materialize, Inc. and contributors. All rights reserved. +// +// Use of this software is governed by the Business Source License +// included in the LICENSE file at the root of this repository. +// +// As of the Change Date specified in that file, in accordance with +// the Business Source License, use of this software will be governed +// by the Apache License, Version 2.0. + +const PASSWORD = process.env.E2E_TEST_PASSWORD; +if (!PASSWORD) process.exit(0); + +const CLOUD_HOST = process.env.CLOUD_HOST || "cloud.materialize.com"; +const STACK = CLOUD_HOST.includes("staging") ? "staging" : "production"; +const stackSuffix = STACK === "production" ? "" : `.${STACK}`; +const FRONTEGG_URL = `https://admin${stackSuffix}.cloud.materialize.com`; +const REGIONS = [ + `https://api.us-east-1.aws${stackSuffix}.cloud.materialize.com`, + `https://api.eu-west-1.aws${stackSuffix}.cloud.materialize.com`, +]; +const NUM_WORKERS = 5; + +async function disableRegionsForUser(email) { + let accessToken; + try { + const res = await fetch( + `${FRONTEGG_URL}/identity/resources/auth/v1/user`, + { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ email, password: PASSWORD }), + signal: AbortSignal.timeout(10_000), + }, + ); + if (!res.ok) return; + ({ accessToken } = await res.json()); + } catch { + return; + } + + await Promise.allSettled( + REGIONS.map((regionUrl) => + fetch(`${regionUrl}/api/region?hardDelete=true`, { + method: "DELETE", + headers: { + authorization: `Bearer ${accessToken}`, + "content-type": "application/json", + }, + signal: AbortSignal.timeout(65_000), + }), + ), + ); +} + +const workers = []; +for (let i = 0; i < NUM_WORKERS; i++) { + const email = `infra+cloud-integration-tests-${STACK}-console-${i}@materialize.io`; + workers.push(disableRegionsForUser(email)); +} +await Promise.allSettled(workers); diff --git a/ci/test/console/console-e2e.sh b/ci/test/console/console-e2e.sh deleted file mode 100755 index 1f72b834ed6d6..0000000000000 --- a/ci/test/console/console-e2e.sh +++ /dev/null @@ -1,154 +0,0 @@ -#!/usr/bin/env bash - -# Copyright Materialize, Inc. and contributors. All rights reserved. -# -# Use of this software is governed by the Business Source License -# included in the LICENSE file at the root of this repository. -# -# As of the Change Date specified in that file, in accordance with -# the Business Source License, use of this software will be governed -# by the Apache License, Version 2.0. -# -# console-e2e.sh — Run console end-to-end tests against a local kind cluster -# with the cloud stack. -# -# This script runs directly on the Buildkite agent host (NOT inside ci-builder) -# because it needs sudo for DNS configuration and systemd. Node.js/yarn/kind -# operations are delegated to ci-builder via bin/ci-builder. -# -# Required environment variables: -# GITHUB_TOKEN — GitHub token for GHCR access -# CLOUD_DEPLOY_KEY — Key for cloning the cloud repo -# E2E_TEST_PASSWORD — Frontegg e2e test user password -# E2E_FRONTEGG_CLIENT_ID — Frontegg API client ID -# E2E_FRONTEGG_SECRET_KEY — Frontegg API secret key - -set -euo pipefail - -. misc/shlib/shlib.bash - -cd "$(dirname "$0")/../../.." - -# --- Resolve cloud version --- - -cloud_ref=$( ~/.ssh/cloud_deploy_key -chmod 600 ~/.ssh/cloud_deploy_key -ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null -export GIT_SSH_COMMAND="ssh -i ~/.ssh/cloud_deploy_key -o StrictHostKeyChecking=no" - -ci_collapsed_heading "Cloning cloud repo" -# Clone into workspace so it's accessible inside ci-builder (which mounts CWD) -git clone --depth 100 git@github.com:MaterializeInc/cloud.git .cloud-repo - -if [[ "$cloud_ref" == "__LATEST__" ]]; then - ci_collapsed_heading "Resolving latest cloud docker tag" - cloud_ref=$(bin/ci-builder run stable bash -c "cd .cloud-repo && node console/bin/latest-cloud-docker.js") -fi - -echo "Cloud ref: $cloud_ref" -export MZCLOUD_DOCKER_COMPOSE_TAG="$cloud_ref" - -# Re-checkout cloud at the exact ref -cd .cloud-repo -git fetch origin "$cloud_ref" -git checkout "$cloud_ref" -git submodule update --init --recursive -cd .. - -# --- Pull cloud image (host — has Docker) --- - -ci_collapsed_heading "Pulling cloud image" -echo "$GITHUB_TOKEN" | docker login ghcr.io -u "materialize-bot" --password-stdin -docker pull "ghcr.io/materializeinc/cloud:$cloud_ref" - -# --- DNS setup (host — needs sudo) --- - -ci_collapsed_heading "Configuring DNS for kind cluster" -sudo apt-get update -qq -sudo apt-get install -y -qq dnsmasq > /dev/null 2>&1 || true - -sudo cp console/misc/dnsmasq/mzcloud-kind.conf /etc/dnsmasq.d/mzcloud-kind.conf -sudo bash -c 'echo "port=5353" >> /etc/dnsmasq.d/mzcloud-kind.conf' -sudo bash -c 'echo "no-resolv" >> /etc/dnsmasq.d/mzcloud-kind.conf' -sudo bash -c 'echo "server=8.8.8.8" >> /etc/dnsmasq.d/mzcloud-kind.conf' -sudo bash -c 'echo "127.0.0.1 mzcloud-control-plane" >> /etc/hosts' -sudo systemctl restart dnsmasq.service || true -sudo sed -i 's/#DNS=/DNS=127.0.0.1:5353/' /etc/systemd/resolved.conf -sudo systemctl restart systemd-resolved || true - -# --- Kind cluster (ci-builder — has kind, kubectl, Python) --- - -ci_collapsed_heading "Setting up cloud kind cluster" - -# Extract Frontegg JWK from cloud config (ci-builder has node) -frontegg_jwk=$(bin/ci-builder run stable node -e " - const d = require('./.cloud-repo/config/settings/local.outputs.json'); - process.stdout.write(d.frontegg_jwk); -") -# Extract environmentd image ref from cloud Pulumi config -environmentd_image_ref=$(grep '^ mzcloud:environmentd_image_ref:' .cloud-repo/Pulumi.staging.yaml | sed 's/.*: //') - -# Create kind cluster using cloud's setup scripts -bin/ci-builder run stable bash -c " - cd .cloud-repo - bin/helpers/pyactivate /dev/null - FRONTEGG_URL=https://admin.staging.cloud.materialize.com \ - FRONTEGG_JWK_STRING='${frontegg_jwk}' \ - ENVIRONMENTD_IMAGE_REF_DEFAULT='${environmentd_image_ref}' \ - bin/kind-create -" - -# --- Console setup (ci-builder — has node/yarn/corepack) --- - -ci_collapsed_heading "Installing console dependencies" -bin/ci-builder run stable bash -c " - cd console - export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 - corepack enable - yarn install --immutable --network-timeout 30000 - yarn playwright install --with-deps -" - -# --- Run tests --- - -ci_collapsed_heading "Starting console dev server" -bin/ci-builder run stable --detach --name console-dev-server bash -c " - cd console - export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 - corepack enable - DEFAULT_STACK=local yarn start -" - -# Wait for dev server to be ready (host — ci-builder uses --network host) -for i in $(seq 1 60); do - if curl -s http://localhost:3000 > /dev/null 2>&1; then - echo "Dev server is ready" - break - fi - if [[ $i -eq 60 ]]; then - echo "Dev server failed to start within 60 seconds" - exit 1 - fi - sleep 1 -done - -ci_uncollapsed_heading "Running console e2e tests" -e2e_exit=0 -set -o pipefail -bin/ci-builder run stable bash -c " - cd console - export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 - corepack enable - CONSOLE_ADDR=http://local.dev.materialize.com:3000 yarn test:e2e:all -" | tee run.log || e2e_exit=$? - -# --- Cleanup --- - -docker stop console-dev-server 2>/dev/null || true -docker rm console-dev-server 2>/dev/null || true - -exit "$e2e_exit" diff --git a/ci/test/console/console-e2e-prod.sh b/ci/test/console/e2e-prod.sh similarity index 59% rename from ci/test/console/console-e2e-prod.sh rename to ci/test/console/e2e-prod.sh index abcf34ea0b476..352564e97d5f4 100755 --- a/ci/test/console/console-e2e-prod.sh +++ b/ci/test/console/e2e-prod.sh @@ -15,15 +15,28 @@ # This mirrors the old GHA merge_queue.yml e2e-tests job from the console repo. # # Required environment variables: -# VERCEL_TOKEN — Vercel API token -# VERCEL_ORG_ID — Vercel organization ID -# VERCEL_PROJECT_ID — Vercel project ID -# E2E_TEST_PASSWORD — Frontegg e2e test user password (production) +# VERCEL_TOKEN — Vercel API token +# VERCEL_ORG_ID — Vercel organization ID +# VERCEL_PROJECT_ID — Vercel project ID +# CONSOLE_E2E_TEST_PRODUCTION_PASSWORD — Frontegg e2e test user password (production) set -euo pipefail . misc/shlib/shlib.bash +repo_root=$(pwd) +cleanup() { + ci_collapsed_heading "Cleaning up: disabling regions" + node "$repo_root/ci/test/console/cleanup-regions.mjs" 2>/dev/null || true +} +trap cleanup EXIT + +# Disable any regions left over from a previous run that didn't exit cleanly. +ci_collapsed_heading "Cleaning up stale regions" +cleanup + +export E2E_TEST_PASSWORD="$CONSOLE_E2E_TEST_PRODUCTION_PASSWORD" + cd console export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 @@ -31,21 +44,19 @@ corepack enable ci_collapsed_heading "Installing dependencies" yarn install --immutable --network-timeout 30000 -yarn playwright install --with-deps -npm install --global vercel@latest - +yarn playwright install ci_collapsed_heading "Pulling Vercel production environment" -vercel pull --yes --environment=production --token="$VERCEL_TOKEN" +npx vercel@latest pull --yes --environment=production --token="$VERCEL_TOKEN" mv .vercel/.env.production.local .vercel/.env.preview.local ci_collapsed_heading "Building with Vercel production environment" export SENTRY_RELEASE="$BUILDKITE_COMMIT" export FORCE_OVERRIDE_STACK=production bin/apply-vercel-csp.js -vercel build --token="$VERCEL_TOKEN" +npx vercel@latest build --token="$VERCEL_TOKEN" ci_collapsed_heading "Deploying to Vercel preview" -CONSOLE_ADDR=$(vercel deploy --prebuilt --token="$VERCEL_TOKEN") +CONSOLE_ADDR=$(npx vercel@latest deploy --prebuilt --token="$VERCEL_TOKEN") export CONSOLE_ADDR echo "Console deployed at: $CONSOLE_ADDR" @@ -53,6 +64,6 @@ ci_uncollapsed_heading "Running console e2e tests (production)" export CLOUD_HOST=cloud.materialize.com e2e_exit=0 set -o pipefail -yarn test:e2e:all | tee run.log || e2e_exit=$? +yarn test:e2e:all | ../ci/test/console/embed-test-results.sh | tee run.log || e2e_exit=$? exit "$e2e_exit" diff --git a/ci/test/console/e2e.sh b/ci/test/console/e2e.sh new file mode 100755 index 0000000000000..40ffd88efe358 --- /dev/null +++ b/ci/test/console/e2e.sh @@ -0,0 +1,194 @@ +#!/usr/bin/env bash + +# Copyright Materialize, Inc. and contributors. All rights reserved. +# +# Use of this software is governed by the Business Source License +# included in the LICENSE file at the root of this repository. +# +# As of the Change Date specified in that file, in accordance with +# the Business Source License, use of this software will be governed +# by the Apache License, Version 2.0. +# +# e2e.sh — Run console end-to-end tests against a local kind cluster +# with the cloud stack. +# +# Required environment variables: +# GITHUB_GHCR_TOKEN — GitHub token for GHCR access +# CLOUD_DEPLOY_KEY — Key for cloning the cloud repo +# CONSOLE_E2E_TEST_STAGING_PASSWORD — Frontegg e2e test user password (staging) +# MZ_CI_LICENSE_KEY — Materialize license key +# E2E_FRONTEGG_CLIENT_ID — Frontegg API client ID +# E2E_FRONTEGG_SECRET_KEY — Frontegg API secret key + +set -euo pipefail + +. misc/shlib/shlib.bash + +dev_server_pid= +# shellcheck disable=SC2317 # invoked indirectly via trap +k8s_diagnostics() { + ci_collapsed_heading "K8s diagnostics" + local kc="kubectl --context kind-mzcloud" + + echo "=== All pods ===" + $kc get pods -A -o wide 2>&1 || true + + echo "=== Materialize CRs ===" + $kc get materialize -A -o wide 2>&1 || true + + echo "=== Events (all namespaces) ===" + $kc get events -A --sort-by='.lastTimestamp' 2>&1 || true + + # Collect logs from each mz-system pod individually + for pod in $($kc get pods -n mz-system -o name 2>/dev/null); do + echo "=== Logs: mz-system/$pod ===" + $kc logs -n mz-system "$pod" --tail=200 --timestamps 2>&1 || true + done + + echo "=== Logs: cockroachdb ===" + $kc logs -n cockroachdb cockroachdb-0 --tail=50 --timestamps 2>&1 || true + + # Dump environmentd pods if any exist (created when a region is enabled) + while IFS=' ' read -r ns pod; do + echo "=== Describe: $ns/$pod ===" + $kc describe pod -n "$ns" "$pod" 2>&1 || true + echo "=== Logs: $ns/$pod ===" + $kc logs -n "$ns" "$pod" --all-containers --tail=200 --timestamps 2>&1 || true + done < <($kc get pods -A -o wide 2>/dev/null | grep -i environmentd | awk '{print $1, $2}') +} + +# shellcheck disable=SC2317 # invoked indirectly via trap +cleanup() { + k8s_diagnostics + ci_collapsed_heading "Cleaning up" + if [[ -n "$dev_server_pid" ]]; then kill "$dev_server_pid" 2>/dev/null || true; fi + kind delete cluster --name mzcloud 2>/dev/null || true + docker rm -f proxy-docker-hub 2>/dev/null || true + docker network rm kind 2>/dev/null || true +} +trap cleanup EXIT + +# Clean up stale resources from a previous run that may not have exited cleanly. +ci_collapsed_heading "Cleaning up stale resources" +# Skip diagnostics during initial cleanup — there's no cluster to inspect yet. +kind delete cluster --name mzcloud 2>/dev/null || true +docker rm -f proxy-docker-hub 2>/dev/null || true +docker network rm kind 2>/dev/null || true + +export E2E_TEST_PASSWORD="$CONSOLE_E2E_TEST_STAGING_PASSWORD" +export MATERIALIZE_LICENSE_KEY="$MZ_CI_LICENSE_KEY" + +# --- Resolve cloud version --- + +cloud_ref=$( ~/.ssh/cloud_deploy_key +chmod 600 ~/.ssh/cloud_deploy_key +ssh-keyscan github.com >> ~/.ssh/known_hosts 2>/dev/null +export GIT_SSH_COMMAND="ssh -i ~/.ssh/cloud_deploy_key -o StrictHostKeyChecking=no" + +ci_collapsed_heading "Logging into GHCR" +echo "$GITHUB_GHCR_TOKEN" | docker login ghcr.io -u "materialize-bot" --password-stdin + +ci_collapsed_heading "Cloning cloud repo" +git clone --depth 100 git@github.com:MaterializeInc/cloud.git .cloud-repo + +if [[ "$cloud_ref" == "__LATEST__" ]]; then + ci_collapsed_heading "Resolving latest cloud docker tag" + cloud_ref=$(cd .cloud-repo && node ../console/bin/latest-cloud-docker.js) +fi + +echo "Cloud ref: $cloud_ref" +export MZCLOUD_DOCKER_COMPOSE_TAG="$cloud_ref" + +# Re-checkout cloud at the exact ref +cd .cloud-repo +git fetch --depth 1 origin "$cloud_ref" +git checkout "$cloud_ref" +git submodule update --init --recursive +cd .. + +# --- Pull cloud image --- + +ci_collapsed_heading "Pulling cloud image" +docker pull "ghcr.io/materializeinc/cloud:$cloud_ref" + +# --- Kind cluster --- + +ci_collapsed_heading "Setting up cloud kind cluster" + +# Extract Frontegg JWK from cloud config +frontegg_jwk=$(node -e " + const d = require('./.cloud-repo/config/settings/local.outputs.json'); + process.stdout.write(d.frontegg_jwk); +" | perl -pe 's/\n/\\n/g') +# Extract environmentd image ref from cloud Pulumi config +environmentd_image_ref=$(grep '^ mzcloud:environmentd_image_ref:' .cloud-repo/Pulumi.staging.yaml | sed 's/.*: //') + +# Create kind cluster using cloud's setup scripts +cd .cloud-repo +bin/helpers/pyactivate /dev/null +FRONTEGG_URL=https://admin.staging.cloud.materialize.com \ +FRONTEGG_JWK_STRING="${frontegg_jwk}" \ +ENVIRONMENTD_IMAGE_REF_DEFAULT="${environmentd_image_ref}" \ + bin/kind-create +cd .. + +# --- DNS setup (needs sudo) --- +# The CI container doesn't have systemd, so we run dnsmasq on port 53 as a +# transparent forwarder that adds our custom wildcard record. We prepend +# 127.0.0.1 to /etc/resolv.conf so dnsmasq is tried first, keeping the +# original upstream nameservers as fallback for dnsmasq itself. + +ci_collapsed_heading "Configuring DNS for kind cluster" +sudo bash -c 'echo "127.0.0.1 mzcloud-control-plane" >> /etc/hosts' +# Save the current upstream nameserver before we modify resolv.conf. +upstream_ns=$(grep '^nameserver' /etc/resolv.conf | head -1 | awk '{print $2}') +: "${upstream_ns:=8.8.8.8}" +# Run dnsmasq on port 53, forwarding unknown queries to the original upstream. +sudo dnsmasq \ + --address=/lb.testing.materialize.cloud/127.0.0.1 \ + --server="$upstream_ns" \ + --no-resolv \ + --listen-address=127.0.0.1 \ + --bind-interfaces +# Prepend 127.0.0.1 so the system uses dnsmasq first. +# /etc/resolv.conf may be a Docker mount, so overwrite in-place instead of mv. +sudo bash -c 'cp /etc/resolv.conf /tmp/resolv.conf.bak && { echo "nameserver 127.0.0.1"; cat /tmp/resolv.conf.bak; } > /etc/resolv.conf' + +# --- Console setup --- + +ci_collapsed_heading "Installing console dependencies" +cd console +export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +corepack enable +yarn install --immutable --network-timeout 30000 +yarn playwright install + +# --- Run tests --- + +ci_collapsed_heading "Starting console dev server" +DEFAULT_STACK=local yarn start & +dev_server_pid=$! + +# Wait for dev server to be ready +for i in $(seq 1 60); do + if curl -s http://localhost:3000 > /dev/null 2>&1; then + echo "Dev server is ready" + break + fi + if [[ $i -eq 60 ]]; then + echo "Dev server failed to start within 60 seconds" + exit 1 + fi + sleep 1 +done + +ci_uncollapsed_heading "Running console e2e tests" +e2e_exit=0 +set -o pipefail +CONSOLE_ADDR=http://local.dev.materialize.com:3000 yarn test:e2e:all | ../ci/test/console/embed-test-results.sh | tee run.log || e2e_exit=$? + +exit "$e2e_exit" diff --git a/ci/test/console/embed-test-results.sh b/ci/test/console/embed-test-results.sh new file mode 100755 index 0000000000000..2e4d1d7bf4033 --- /dev/null +++ b/ci/test/console/embed-test-results.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +# Copyright Materialize, Inc. and contributors. All rights reserved. +# +# Use of this software is governed by the Business Source License +# included in the LICENSE file at the root of this repository. +# +# As of the Change Date specified in that file, in accordance with +# the Business Source License, use of this software will be governed +# by the Apache License, Version 2.0. +# +# embed-test-results.sh — Pipe filter that detects Playwright attachment +# paths in test output, uploads them as Buildkite artifacts, and injects +# inline image / link escape sequences right after the path line. +# +# Usage: yarn test:e2e:all | ../ci/test/console/embed-test-results.sh | tee run.log + +has_buildkite=false +command -v buildkite-agent &>/dev/null && has_buildkite=true + +declare -A uploaded_dirs + +while IFS= read -r line; do + if $has_buildkite; then + # Suppress "attachment #N: type ────" header lines. + [[ "$line" =~ ^[[:space:]]*attachment\ \#[0-9]+: ]] && continue + # Suppress "────" separator lines. + [[ "$line" =~ ^[[:space:]]*─+[[:space:]]*$ ]] && continue + fi + + echo "$line" + + # Match lines that are just a test-results artifact path (with optional whitespace). + if [[ "$line" =~ ^[[:space:]]*(test-results/[^[:space:]]+\.(png|webm|zip))[[:space:]]*$ ]]; then + filepath="${BASH_REMATCH[1]}" + [[ -f "$filepath" ]] || continue + dir=$(dirname "$filepath") + + if $has_buildkite; then + # Upload the entire failure directory on first encounter. + if [[ -z "${uploaded_dirs[$dir]:-}" ]]; then + uploaded_dirs[$dir]=1 + buildkite-agent artifact upload "${dir}/*" 2>/dev/null + fi + + test_name=$(basename "$dir") + case "$filepath" in + *.png) + printf "\033]1338;url='artifact://%s';alt='%s'\a\n" "$filepath" "$test_name" + ;; + *.webm) + printf "\033]1339;url='artifact://%s';content='Video: %s'\a\n" "$filepath" "$test_name" + ;; + *.zip) + printf "\033]1339;url='artifact://%s';content='Trace: %s'\a\n" "$filepath" "$test_name" + ;; + esac + fi + fi +done diff --git a/ci/test/console/console-lint.sh b/ci/test/console/lint.sh similarity index 100% rename from ci/test/console/console-lint.sh rename to ci/test/console/lint.sh diff --git a/ci/test/console/console-test.sh b/ci/test/console/test.sh similarity index 100% rename from ci/test/console/console-test.sh rename to ci/test/console/test.sh diff --git a/ci/test/console/vercel-preview.sh b/ci/test/console/vercel-preview.sh new file mode 100755 index 0000000000000..641cba3b31312 --- /dev/null +++ b/ci/test/console/vercel-preview.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +# Copyright Materialize, Inc. and contributors. All rights reserved. +# +# Use of this software is governed by the Business Source License +# included in the LICENSE file at the root of this repository. +# +# As of the Change Date specified in that file, in accordance with +# the Business Source License, use of this software will be governed +# by the Apache License, Version 2.0. + +set -euo pipefail + +cd console +export COREPACK_ENABLE_DOWNLOAD_PROMPT=0 +export VERCEL_ENVIRONMENT=preview +export SENTRY_RELEASE="$BUILDKITE_COMMIT" +corepack enable +yarn install --immutable --network-timeout 30000 + +npx vercel@latest pull --yes --environment="$VERCEL_ENVIRONMENT" --token="$VERCEL_TOKEN" +npx vercel@latest build --token="$VERCEL_TOKEN" + +deployment_url="$(npx vercel@latest deploy --prebuilt --token="$VERCEL_TOKEN")" +alias_url="$(node bin/vercel-preview-url.js "${BUILDKITE_BRANCH}")" +echo "Aliasing $deployment_url to $alias_url" +npx vercel@latest alias --scope=materialize --token="$VERCEL_TOKEN" set "$deployment_url" "$alias_url" +printf "+++ Console preview: \033]1339;url='https://%s'\a\n" "$alias_url" diff --git a/ci/test/pipeline.template.yml b/ci/test/pipeline.template.yml index 0bb1efe562aaa..743aaa49c5e34 100644 --- a/ci/test/pipeline.template.yml +++ b/ci/test/pipeline.template.yml @@ -773,8 +773,8 @@ steps: key: console-tests steps: - id: console-lint - label: ":npm: Console lint and typecheck" - command: bin/ci-builder run stable ci/test/console/console-lint.sh + label: "Console lint and typecheck" + command: bin/ci-builder run stable ci/test/console/lint.sh inputs: - console/** - ci/test/console/** @@ -786,8 +786,8 @@ steps: sanitizer: skip - id: console-test - label: ":npm: Console unit tests" - command: bin/ci-builder run stable ci/test/console/console-test.sh + label: "Console unit tests" + command: bin/ci-builder run stable ci/test/console/test.sh inputs: - console/** - ci/test/console/** @@ -795,12 +795,12 @@ steps: timeout_in_minutes: 15 artifact_paths: console/log/**/* agents: - queue: hetzner-aarch64-4cpu-8gb + queue: hetzner-aarch64-8cpu-16gb coverage: skip sanitizer: skip - id: console-sql-test - label: ":npm: Console SQL tests" + label: "Console SQL tests" depends_on: build-x86_64 timeout_in_minutes: 30 inputs: @@ -817,38 +817,57 @@ steps: sanitizer: skip - id: console-e2e-test - label: ":playwright: Console e2e tests" - command: ci/test/console/console-e2e.sh + label: "Console E2E against staging" + command: bin/ci-builder run console ci/test/console/e2e.sh inputs: - console/** - ci/test/console/** depends_on: [] timeout_in_minutes: 60 - artifact_paths: console/test-results/**/* + concurrency: 1 + concurrency_group: "console-e2e-staging" + artifact_paths: console/run.log agents: - queue: linux-x86_64-large + queue: linux-x86_64 env: CI: "true" coverage: skip sanitizer: skip - skip: "Get secrets from console repo" + ci_glue_exempt: true - - id: console-e2e-prod-test - label: ":playwright: Console e2e tests (production)" - command: ci/test/console/console-e2e-prod.sh + - id: console-e2e-test-prod + label: "Console E2E against production" + command: bin/ci-builder run console ci/test/console/e2e-prod.sh inputs: - console/** - ci/test/console/** depends_on: [] timeout_in_minutes: 60 - artifact_paths: console/test-results/**/* + concurrency: 1 + concurrency_group: "console-e2e-prod" + artifact_paths: console/run.log agents: - queue: linux-x86_64-large + queue: linux-x86_64 env: CI: "true" coverage: skip sanitizer: skip - skip: "Requires Vercel secrets (VERCEL_TOKEN, VERCEL_ORG_ID, VERCEL_PROJECT_ID) to be configured in Buildkite" + ci_glue_exempt: true + + - id: console-vercel-preview + label: ":vercel: Console Vercel preview" + command: bin/ci-builder run stable ci/test/console/vercel-preview.sh + inputs: + - console/** + depends_on: [] + timeout_in_minutes: 15 + if: "build.pull_request.id != null" + + agents: + queue: linux-x86_64 + coverage: skip + sanitizer: skip + ci_glue_exempt: true - id: deploy-website label: Deploy website diff --git a/console/CLAUDE.md b/console/CLAUDE.md index 47bef654b9469..f5d2f5d533e19 100644 --- a/console/CLAUDE.md +++ b/console/CLAUDE.md @@ -1,6 +1,6 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +This file provides guidance to Claude Code (claude.ai/code) when working with code in this directory. ## Build and Development Commands @@ -47,7 +47,8 @@ yarn gen:api yarn gen:types # Optional: Enable pre-commit hook to auto-run ESLint --fix on staged files -ln -s ../../misc/githooks/pre-commit .git/hooks/pre-commit +# Run from the materialize repo root: +ln -s ../../console/misc/githooks/pre-commit .git/hooks/pre-commit ``` ## Architecture Overview diff --git a/console/README.md b/console/README.md index b975b45c9c447..2a514e627ad40 100644 --- a/console/README.md +++ b/console/README.md @@ -4,19 +4,19 @@ This is the web console interface for Materialize. Doc index -- [Self-hosted Console Impersonation](/misc/docker/README.md) - describes +- [Self-hosted Console Impersonation](misc/docker/README.md) - describes how to build and deploy a self-hosted version of the Console. -- [CI](/doc/CI.md) - a high level overview of our CI/CD pipeline. -- [Architecture](/doc/architecture.md) - a general overview of how Console +- [CI](doc/CI.md) - a high level overview of our CI/CD pipeline. +- [Architecture](doc/architecture.md) - a general overview of how Console talks to the rest of the Materialize. -- [Testing guide](/doc/guide-testing.md) - a guide for testing in the Console. -- [Internal apps](/doc/internal-apps.md) - how to build internal only features +- [Testing guide](doc/guide-testing.md) - a guide for testing in the Console. +- [Internal apps](doc/internal-apps.md) - how to build internal only features in console. -- [SSO/Frontegg Debugging](/doc/testing-sso.md) - how to test OpenID Connect, SAML, and anything that can be done in Frontegg staging/production but not in local development. -- [Mz Backwards Compatibility](/doc/mz-backwards-compatibility.md) - How to handle backwards compatibility during Materialize version changes -- [Organization Impersonation](/doc/organization-impersonation.md) - describes +- [SSO/Frontegg Debugging](doc/testing-sso.md) - how to test OpenID Connect, SAML, and anything that can be done in Frontegg staging/production but not in local development. +- [Mz Backwards Compatibility](doc/mz-backwards-compatibility.md) - How to handle backwards compatibility during Materialize version changes +- [Organization Impersonation](doc/organization-impersonation.md) - describes our how console can impersonate customer organizations. -- [Design docs](/doc/design/README.md) - details about our technical design +- [Design docs](doc/design/README.md) - details about our technical design doc process. ## Running the app locally @@ -39,7 +39,7 @@ Volta-distributed Node](https://github.com/volta-cli/volta/issues/987). ### Cloud setup -Clone the [Cloud repo](MaterializeInc/cloud) as a sibling to this repo. We rely +Clone the [Cloud repo](https://github.com/MaterializeInc/cloud) as a sibling to the materialize repo. We rely on cloud for a few things: - The mzadmin tool for getting cli access to AWS and configuring our k8s @@ -192,7 +192,8 @@ To automatically run ESLint on staged files before each commit, you can enable the pre-commit hook: ```bash -ln -s ../../misc/githooks/pre-commit .git/hooks/pre-commit +# From the materialize repo root: +ln -s ../../console/misc/githooks/pre-commit .git/hooks/pre-commit ``` ### Component files diff --git a/console/bin/apply-vercel-csp.js b/console/bin/apply-vercel-csp.js old mode 100644 new mode 100755 diff --git a/console/bin/build-ci b/console/bin/build-ci old mode 100644 new mode 100755 diff --git a/console/bin/check-trufflehog-logs b/console/bin/check-trufflehog-logs old mode 100644 new mode 100755 diff --git a/console/bin/check-trufflehog-repo b/console/bin/check-trufflehog-repo old mode 100644 new mode 100755 diff --git a/console/bin/generate-types b/console/bin/generate-types old mode 100644 new mode 100755 diff --git a/console/bin/latest-cloud-docker.js b/console/bin/latest-cloud-docker.js old mode 100644 new mode 100755 index 2b7e2fb6a8b4f..cfe142eadb7ce --- a/console/bin/latest-cloud-docker.js +++ b/console/bin/latest-cloud-docker.js @@ -22,7 +22,9 @@ async function getCommitShas() { } async function checkDockerTag(tag) { - const encodedToken = Buffer.from(process.env.GITHUB_TOKEN).toString("base64"); + const encodedToken = Buffer.from(process.env.GITHUB_GHCR_TOKEN).toString( + "base64", + ); const response = await fetch( `https://ghcr.io/v2/materializeinc/cloud/manifests/${tag}`, { diff --git a/console/bin/latest-materialize-docker.js b/console/bin/latest-materialize-docker.js old mode 100644 new mode 100755 diff --git a/console/bin/vercel-preview-url.js b/console/bin/vercel-preview-url.js old mode 100644 new mode 100755 diff --git a/console/doc/CI.md b/console/doc/CI.md index b16aa127cf15c..dc5bd8e4fe6b7 100644 --- a/console/doc/CI.md +++ b/console/doc/CI.md @@ -1,6 +1,6 @@ # CI / CD pipelines -Our CI / CD uses Github Actions. +Our CI / CD uses Buildkite as part of the main materialize pipeline. ## PR CI diff --git a/console/doc/design/20241004_freshness.md b/console/doc/design/20241004_freshness.md index 7016dced94661..640f02bd5cc20 100644 --- a/console/doc/design/20241004_freshness.md +++ b/console/doc/design/20241004_freshness.md @@ -36,17 +36,17 @@ The objects that the user will most likely care about are whatever’s directly We’ll have a new Freshness card that’ll appear in the environment overview dashboard, cluster overview dashboard, and a new tab in an object’s detail page labelled “Freshness”: -![image.png](/doc/design/20241004_freshness/freshness_card.png) +![image.png](20241004_freshness/freshness_card.png) The freshness card will consist of a time filter with options `last hour, last 6 hours, last 24 hours`. We map each object (depending on the dashboard) on a line graph on the left and we show the current/latest lag as a table on the right. The table will be sorted by lag descending. The table serves as navigation, a place to get current values, and follows a similar design to the resource intensive object table. In terms of components and actual design, we’ll reuse as much as we can from our cluster overview dashboard and figure out a table variant that’ll look good in a card. The following are my inspirations for this layout: Grafana: -![Screenshot 2024-09-04 at 1.37.58 PM.png](/doc/design/20241004_freshness/grafana.png) +![Screenshot 2024-09-04 at 1.37.58 PM.png](20241004_freshness/grafana.png) Datadog: -![Screenshot 2024-10-04 at 5.36.50 AM.png](/doc/design/20241004_freshness/datadog.png) +![Screenshot 2024-10-04 at 5.36.50 AM.png](20241004_freshness/datadog.png) ### Environment overview dashboard @@ -152,7 +152,7 @@ We can’t show all objects of a cluster because for some customers(i.e. General Ideally the table will look like the following, sorted by lag descending: -![Screenshot 2024-10-04 at 5.46.37 AM.png](/doc/design/20241004_freshness/table.png) +![Screenshot 2024-10-04 at 5.46.37 AM.png](20241004_freshness/table.png) Then when users want to debug further, they can navigate to its Freshness tab. @@ -160,7 +160,7 @@ For the SQL queries, it’ll essentially look like the environment dashboard que Here’s a rough sketch of how I see this all fitting together in the Cluster overview page. -![IMG_3499.heic](/doc/design/20241004_freshness/layout_sketch.png) +![IMG_3499.heic](20241004_freshness/layout_sketch.png) We move the “Memory intensive objects” table into the “Resource Usage” card and remove the “Lag behind source” column. We’ll also replace “Lag behind source” columns in each Cluster subpage with our new freshness metric. @@ -168,11 +168,11 @@ We move the “Memory intensive objects” table into the “Resource Usage” c The freshness tab will consist of a graph with the lag of the object and its current lag. -![image.png](/doc/design/20241004_freshness/object_lag_card.png) +![image.png](20241004_freshness/object_lag_card.png) Ideally we’d add a freshness workflow graph below like in the previous design doc below: -![image.png](/doc/design/20241004_freshness/workflow.png) +![image.png](20241004_freshness/workflow.png) but I’ll discuss why this is out of scope later on. diff --git a/console/doc/design/README.md b/console/doc/design/README.md index 95fa1cac05df1..b1b15e3a24f2b 100644 --- a/console/doc/design/README.md +++ b/console/doc/design/README.md @@ -13,6 +13,6 @@ See the [README in the MaterializeInc/materialize repo]( You can also check out recent design doc examples in that subdirectory. ## Additional Console-Specific Guidance -- Create your console design documents within the console repo, in this directory (`doc/design/`). +- Create your console design documents within the `console/doc/design/` directory. - Follow the console-specific template [`00000000_template.md`](00000000_template.md). - For all user-facing features, share your design doc with the appropriate designer and PM as well. You should also collaborate with the designer and PM as you write the design doc where applicable, to define the problem and iterate on the MVP and mockups. diff --git a/console/doc/guide-testing.md b/console/doc/guide-testing.md index 95beb1d3e1e32..7cd4115b730f5 100644 --- a/console/doc/guide-testing.md +++ b/console/doc/guide-testing.md @@ -17,11 +17,10 @@ To run our suite, run `yarn test`. SQL tests require the [console mzcompose](https://github.com/MaterializeInc/materialize/pull/26600) workflow and Docker to be -running. Also, the materialize repo must be cloned in the same directory as console, -since we use a relative path to call `mzcompose`. +running. ```shell -cd ../materialize/test/console +cd ../test/console ./mzcompose run default cd - yarn test:sql @@ -32,7 +31,7 @@ Once the workflow is running, you generally shouldn't have to restart it. When you are done, you can stop the docker containers: ```shell -cd ../materialize/test/console +cd ../test/console ./mzcompose down ``` @@ -96,7 +95,7 @@ Address: 127.0.0.1 ### Running E2E tests -To start, follow the [Cloud Setup](/README.md#cloud-setup) to set up your local stack. +To start, follow the [Cloud Setup](../README.md#cloud-setup) to set up your local stack. Before attempting to run these tests, you should make sure you've pulled the latest cloud changes and docker images (or build the docker images locally. One wrinkle here is @@ -105,7 +104,7 @@ to check out a previous commit, or build updated images. The docker images are t with the commit SHA, so it's easy to check for the latest SHA in the [Github Pacakges repo](https://github.com/MaterializeInc/cloud/pkgs/container/cloud). -First, in `/console`, install all playwright dependencies if you haven't already. +First, in the `console/` directory, install all playwright dependencies if you haven't already. ```shell yarn playwright install --with-deps @@ -121,7 +120,7 @@ DEFAULT_STACK=local yarn start Then, in another terminal, use the cloud scripts to bring up the local stack: ```shell -cd ../cloud +cd ../../cloud # Activate the python venv, required for some cloud scripts source venv/bin/activate # Export necessary variables for staging Frontegg @@ -135,10 +134,10 @@ bin/kind-delete && bin/kind-create In yet another terminal: ```shell -cd ../cloud +cd ../../cloud # Export the test user password export E2E_TEST_PASSWORD=$(bin/pulumi stack output --stack materialize/mzcloud/staging --show-secrets console_e2e_test_password) -cd ../console +cd ../materialize/console yarn test:e2e ``` diff --git a/console/e2e-tests/platform.spec.ts b/console/e2e-tests/platform.spec.ts index 546e47bbc829d..86171756406a6 100644 --- a/console/e2e-tests/platform.spec.ts +++ b/console/e2e-tests/platform.spec.ts @@ -75,6 +75,8 @@ async function reactSelectOption(page: Page, elementId: string, value: string) { const TEST_TIMEOUT = 30 * 60 * 1000; const ENABLE_REGION_TIMEOUT = 60 * 1000; +// Region provisioning can take several minutes on production (e.g. aws/eu-west-1). +const REGION_READY_TIMEOUT = 10 * 60 * 1000; for (const region of REGIONS) { test(`use region ${region.id}`, async ({ page, request, testContext }) => { @@ -202,8 +204,9 @@ for (const region of REGIONS) { .getByRole("link", { name: /integrate with your data stack →/i }) .click(); await page.getByRole("link", { name: /open console →/i }).click({ - // This button will be disabled until the region is ready, which can take some time - timeout: ENABLE_REGION_TIMEOUT, + // This button will be disabled until the region is fully provisioned, + // which can take several minutes in production. + timeout: REGION_READY_TIMEOUT, }); await expect(page.getByTestId("shell")).toBeVisible(); diff --git a/console/misc/docker/README.md b/console/misc/docker/README.md index 9beda53a3a7a3..6ea9915c59d08 100644 --- a/console/misc/docker/README.md +++ b/console/misc/docker/README.md @@ -4,7 +4,7 @@ This document describes how to build and run the Materialize Console Docker imag ## Building the Docker Image Locally -To build the Docker image locally, run the following command from the root of the repository: +To build the Docker image locally, run the following command from the `console/` directory: ```bash docker build -f misc/docker/Dockerfile -t console-dev:latest -t console-dev:1.0.0 . @@ -56,42 +56,6 @@ CONSOLE_DEPLOYMENT_MODE='flexible-deployment' DEV_SERVER_PROXY_PORT=8080 yarn st If the Materialize instance uses TLS, you'll need to run `CONSOLE_DEPLOYMENT_MODE='flexible-deployment' DEV_SERVER_WITH_TLS_PROXY='true' yarn start`. -## Deploying the Docker Image to Docker Hub - -### GitHub Actions Workflow - -The GitHub Actions workflow is set up to automatically build and push Docker images to Docker Hub when a new tag is pushed. - -- The workflow file is located at `.github/workflows/dockerhub.yaml`. -- It uses multi-platform builds for `linux/amd64` and `linux/arm64`. - -### Cutting a New Release - -To create a new release, follow these steps: - -1. Tag the main branch: - - ```bash - git tag -a vX.Y.Z -m "vX.Y.Z" - git push origin vX.Y.Z - ``` - -2. This will trigger the GitHub Actions workflow to build and push a new Docker image to Docker Hub with the new version tag. - -## Docker Hub Example Usage - -To pull the latest published image from Docker Hub: - -```bash -docker pull materialize/console:latest -``` - -You can also specify a version: - -```bash -docker pull materialize/console:v0.1.0 -``` - ## Nginx Configuration The Nginx server is pre-configured to serve static assets and proxy `/api` requests to the Materialize endpoint. diff --git a/console/package.json b/console/package.json index 703ce618966a4..7c5a2fe322881 100644 --- a/console/package.json +++ b/console/package.json @@ -12,9 +12,9 @@ "build:local": "SENTRY_RELEASE=' ' yarn build", "build:analyze": "BUNDLE_ANALYZE=1 yarn build:local", "test": "DEBUG_PRINT_LIMIT=100000 vitest", - "test:e2e": "DEBUG=pw:api playwright test --project=chromium --ui", + "test:e2e": "playwright test --project=chromium --ui", "test:sql": "vitest --config vitest.sql.config.js", - "test:e2e:all": "DEBUG=pw:api playwright test", + "test:e2e:all": "playwright test", "lint": "eslint --ignore-path .gitignore --max-warnings 0 .", "lint:fix": "eslint --ignore-path .gitignore --fix .", "playwright": "playwright", diff --git a/console/redocly.yaml b/console/redocly.yaml index e2d26f14e3c20..0760f35005e26 100644 --- a/console/redocly.yaml +++ b/console/redocly.yaml @@ -9,14 +9,14 @@ apis: global: - root: ../cloud/doc/api/global-api.yml + root: ../../cloud/doc/api/global-api.yml x-openapi-ts: output: ./src/api/schemas/global-api.ts internal: - root: ../cloud/doc/api/internal-api.yml + root: ../../cloud/doc/api/internal-api.yml x-openapi-ts: output: ./src/api/schemas/internal-api.ts region: - root: ../cloud/doc/api/region-api.yml + root: ../../cloud/doc/api/region-api.yml x-openapi-ts: output: ./src/api/schemas/region-api.ts From fd1ebca0ea9830fcd15d4a14a46d95624ba3a120 Mon Sep 17 00:00:00 2001 From: Dennis Felsing Date: Thu, 26 Feb 2026 09:10:21 +0000 Subject: [PATCH 2/3] Reorder test/nightly pipeline a bit, shorten names --- ci/mkpipeline.py | 11 +- ci/nightly/pipeline.template.yml | 74 ++++----- .../pipeline.template.yml | 2 +- ci/test/console/e2e.sh | 2 +- ci/test/pipeline.template.yml | 147 +++++++++--------- 5 files changed, 114 insertions(+), 122 deletions(-) diff --git a/ci/mkpipeline.py b/ci/mkpipeline.py index 102dfe25e9801..8326c92a48edb 100644 --- a/ci/mkpipeline.py +++ b/ci/mkpipeline.py @@ -175,9 +175,6 @@ def fetch_hashes() -> None: args.sanitizer, lto, ) - # Steps marked ci_glue_exempt should still be trimmed based on - # their own inputs, even when CI glue code has changed, since - # they don't depend on CI infrastructure (e.g. console e2e tests). trim_ci_glue_exempt_steps(pipeline) else: print("Trimming unchanged steps from pipeline") @@ -505,8 +502,8 @@ def switch_jobs_to_aws(pipeline: Any, priority: int) -> None: step["agents"]["queue"] = "linux-aarch64-medium" elif agent == "hetzner-aarch64-16cpu-32gb": - if "hetzner-x86-64-16cpu-32gb" not in stuck: - step["agents"]["queue"] = "hetzner-x86-64-16cpu-32gb" + if "hetzner-x86-64-12cpu-24gb" not in stuck: + step["agents"]["queue"] = "hetzner-x86-64-12cpu-24gb" if step.get("depends_on") == "build-aarch64": step["depends_on"] = "build-x86_64" else: @@ -1041,10 +1038,6 @@ def have_paths_changed(globs: Iterable[str]) -> bool: def trim_ci_glue_exempt_steps(pipeline: Any) -> None: - """Trim steps marked with ci_glue_exempt based on their own inputs, even - when CI glue code has changed. These steps (e.g. console e2e tests) don't - depend on CI infrastructure and should only run when their declared inputs - have actually changed.""" for step in steps(pipeline): if not step.get("ci_glue_exempt"): continue diff --git a/ci/nightly/pipeline.template.yml b/ci/nightly/pipeline.template.yml index cae67553c2f8d..dd929ed0cb098 100644 --- a/ci/nightly/pipeline.template.yml +++ b/ci/nightly/pipeline.template.yml @@ -283,7 +283,7 @@ steps: key: kafka steps: - id: kafka-matrix - label: Kafka smoke test against previous Kafka versions + label: Previous Kafka versions depends_on: build-aarch64 timeout_in_minutes: 120 parallelism: 2 @@ -295,7 +295,7 @@ steps: skip: "https://github.com/MaterializeInc/database-issues/issues/9510" - id: kafka-multi-broker - label: Kafka multi-broker test + label: Kafka multi-broker depends_on: build-aarch64 timeout_in_minutes: 30 agents: @@ -305,7 +305,7 @@ steps: composition: kafka-multi-broker - id: redpanda-resumption - label: ":panda_face: resumption tests" + label: ":panda_face: resumption" depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -535,7 +535,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: ssh-connection-extended - label: Extended SSH connection tests + label: Extended SSH connection depends_on: build-aarch64 timeout_in_minutes: 45 plugins: @@ -685,11 +685,11 @@ steps: composition: zippy args: [--scenario=AlterConnectionWithKafkaSources, --actions=10000, --max-execution-time=30m] - - group: Source Resumption tests + - group: Source Resumption key: source-resumption-tests steps: - id: kafka-resumption - label: Kafka resumption tests + label: Kafka resumption depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -699,7 +699,7 @@ steps: queue: hetzner-aarch64-8cpu-16gb - id: mysql-cdc-resumption - label: "MySQL CDC resumption tests" + label: MySQL CDC resumption parallelism: 2 depends_on: build-aarch64 timeout_in_minutes: 60 @@ -711,7 +711,7 @@ steps: queue: hetzner-aarch64-8cpu-16gb - id: pg-cdc-resumption - label: "Postgres CDC resumption tests" + label: Postgres CDC resumption parallelism: 2 depends_on: build-aarch64 timeout_in_minutes: 60 @@ -723,7 +723,7 @@ steps: queue: hetzner-aarch64-8cpu-16gb - id: sql-server-resumption-old-syntax - label: "SQL Server CDC resumption old syntax tests" + label: SQL Server CDC resumption old syntax depends_on: build-x86_64 timeout_in_minutes: 60 inputs: [test/sql-server-resumption-old-syntax] @@ -740,7 +740,7 @@ steps: key: cdc-old-source-syntax steps: - id: mysql-cdc-old-syntax - label: MySQL CDC tests (before source versioning) + label: MySQL CDC (before source versioning) depends_on: build-aarch64 timeout_in_minutes: 60 plugins: @@ -750,7 +750,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: mysql-cdc-migration - label: MySQL CDC source-versioning migration tests + label: MySQL CDC source-versioning migration depends_on: build-aarch64 timeout_in_minutes: 360 plugins: @@ -761,7 +761,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: mysql-cdc-resumption-old-syntax - label: MySQL CDC resumption tests (before source versioning) + label: MySQL CDC resumption (before source versioning) depends_on: build-aarch64 timeout_in_minutes: 60 plugins: @@ -771,7 +771,7 @@ steps: queue: hetzner-aarch64-8cpu-16gb - id: mysql-rtr-old-syntax - label: MySQL RTR tests (before source versioning) + label: MySQL RTR (before source versioning) depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -781,7 +781,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: pg-cdc-old-syntax - label: Postgres CDC tests (before source versioning) + label: Postgres CDC (before source versioning) depends_on: build-aarch64 timeout_in_minutes: 60 plugins: @@ -792,7 +792,7 @@ steps: # the mzbuild postgres version will be used, which depends on the Dockerfile specification - id: pg-cdc-migration - label: Postgres CDC source-versioning migration tests + label: Postgres CDC source-versioning migration depends_on: build-aarch64 timeout_in_minutes: 360 plugins: @@ -803,7 +803,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: pg-cdc-resumption-old-syntax - label: Postgres CDC resumption tests (before source versioning) + label: Postgres CDC resumption (before source versioning) depends_on: build-aarch64 timeout_in_minutes: 60 plugins: @@ -813,7 +813,7 @@ steps: queue: hetzner-aarch64-8cpu-16gb - id: pg-cdc-old-syntax-multi-version-upgrade-random - label: Postgres CDC tests (before source versioning, multi-version upgrade, random upgrade path) + label: Postgres CDC (before source versioning, multi-version upgrade, random upgrade path) depends_on: build-aarch64 timeout_in_minutes: 120 plugins: @@ -825,7 +825,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: pg-cdc-old-syntax-multi-version-upgrade-earliest-to-current - label: Postgres CDC tests (before source versioning, multi-version upgrade, earliest to current direct upgrade) + label: Postgres CDC (before source versioning, multi-version upgrade, earliest to current direct upgrade) depends_on: build-aarch64 timeout_in_minutes: 120 plugins: @@ -837,7 +837,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: pg-rtr-old-syntax - label: Postgres RTR tests (before source versioning) + label: Postgres RTR (before source versioning) depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -858,7 +858,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: testdrive-kafka-migration - label: "Testdrive (before Kafka source versioning) migration tests" + label: "Testdrive (before Kafka source versioning) migration" depends_on: build-aarch64 timeout_in_minutes: 180 plugins: @@ -870,7 +870,7 @@ steps: parallelism: 2 - id: sql-server-cdc-old-syntax - label: "SQL Server CDC old syntax tests" + label: SQL Server CDC old syntax depends_on: build-x86_64 timeout_in_minutes: 120 inputs: [test/sql-server-cdc-old-syntax] @@ -2073,7 +2073,7 @@ steps: args: [--runtime=1500, --scenario=0dt-deploy, --threads=8] - id: slow-cluster - label: "Slow Cluster tests" + label: Cluster (slow) depends_on: build-aarch64 timeout_in_minutes: 30 agents: @@ -2093,11 +2093,11 @@ steps: - ./ci/plugins/mzcompose: composition: balancerd - - group: Legacy upgrade tests + - group: Legacy upgrade key: legacy-upgrade steps: - id: legacy-upgrade-git - label: Legacy upgrade tests (last version from git) + label: Legacy upgrade (last version from git) depends_on: build-aarch64 timeout_in_minutes: 60 plugins: @@ -2108,7 +2108,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: legacy-upgrade-docs - label: "Legacy upgrade tests (last version from docs)" + label: Legacy upgrade (last version from docs) depends_on: build-aarch64 timeout_in_minutes: 60 plugins: @@ -2118,7 +2118,7 @@ steps: agents: queue: hetzner-aarch64-4cpu-8gb - - group: Cloud tests + - group: Cloud key: cloudtests steps: - id: cloudtest @@ -2255,7 +2255,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: sqllogictest - label: ":bulb: SQL logic tests (2 replicas)" + label: ":bulb: SLT (2 replicas)" depends_on: build-aarch64 timeout_in_minutes: 480 parallelism: 8 @@ -2268,7 +2268,7 @@ steps: args: [--replicas=2, --parallelism=8] - id: iceberg - label: "Iceberg tests" + label: Iceberg depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -2341,11 +2341,11 @@ steps: args: [--runtime=2800, --scenario=concurrent] skip: "Not stable yet, not clear if this is a product issue" - - group: "Language tests" + - group: Language key: language-tests steps: - id: lang-csharp - label: ":csharp: tests" + label: ":csharp:" depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -2355,7 +2355,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: lang-js - label: ":js: tests" + label: ":js:" depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -2365,7 +2365,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: lang-java - label: ":java: tests" + label: ":java:" depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -2375,7 +2375,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: lang-python - label: ":python: tests" + label: ":python:" depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -2385,7 +2385,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: lang-ruby - label: ":ruby: tests" + label: ":ruby:" depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -2394,7 +2394,7 @@ steps: agents: queue: hetzner-aarch64-4cpu-8gb - - group: "Orchestratord tests" + - group: Orchestratord key: orchestratord-test steps: - id: orchestratord-defaults @@ -2517,7 +2517,7 @@ steps: queue: hetzner-aarch64-16cpu-32gb - id: orchestratord-balancer - label: "Orchestratord balancer CRD tests" + label: Orchestratord balancer CRD artifact_paths: ["mz_debug_*.zip"] depends_on: devel-docker-tags timeout_in_minutes: 120 @@ -2530,7 +2530,7 @@ steps: queue: hetzner-aarch64-16cpu-32gb - id: orchestratord-upgrade-operator - label: "Orchestratord operator upgrade tests" + label: Orchestratord operator upgrade artifact_paths: ["mz_debug_*.zip"] depends_on: devel-docker-tags timeout_in_minutes: 120 diff --git a/ci/release-qualification/pipeline.template.yml b/ci/release-qualification/pipeline.template.yml index 7c9db22bbeaad..2334f8f11d866 100644 --- a/ci/release-qualification/pipeline.template.yml +++ b/ci/release-qualification/pipeline.template.yml @@ -171,7 +171,7 @@ steps: label: "Feature benchmark against 'common-ancestor' with --scale=+1" depends_on: build-x86_64 timeout_in_minutes: 2880 - parallelism: 8 + parallelism: 4 agents: queue: hetzner-x86-64-dedi-16cpu-64gb plugins: diff --git a/ci/test/console/e2e.sh b/ci/test/console/e2e.sh index 40ffd88efe358..658d4b55afc01 100755 --- a/ci/test/console/e2e.sh +++ b/ci/test/console/e2e.sh @@ -117,7 +117,7 @@ docker pull "ghcr.io/materializeinc/cloud:$cloud_ref" # --- Kind cluster --- -ci_collapsed_heading "Setting up cloud kind cluster" +ci_collapsed_heading "Setting up cloud kind cluster (~10 min)" # Extract Frontegg JWK from cloud config frontegg_jwk=$(node -e " diff --git a/ci/test/pipeline.template.yml b/ci/test/pipeline.template.yml index 743aaa49c5e34..22fea0a791475 100644 --- a/ci/test/pipeline.template.yml +++ b/ci/test/pipeline.template.yml @@ -242,6 +242,19 @@ steps: coverage: skip sanitizer: skip + - id: console-lint + label: "Console lint and typecheck" + command: bin/ci-builder run stable ci/test/console/lint.sh + inputs: + - console/** + - ci/test/console/** + depends_on: [] + timeout_in_minutes: 15 + agents: + queue: hetzner-aarch64-4cpu-8gb + coverage: skip + sanitizer: skip + - id: cargo-test label: ":rust: Cargo test" @@ -282,19 +295,8 @@ steps: agents: queue: hetzner-aarch64-8cpu-16gb - - id: fdb - label: "FoundationDB" - depends_on: build-aarch64 - timeout_in_minutes: 40 - inputs: [test/foundationdb] - plugins: - - ./ci/plugins/mzcompose: - composition: foundationdb - agents: - queue: hetzner-aarch64-4cpu-8gb - - id: cluster-tests - label: "Cluster tests" + label: Cluster depends_on: build-aarch64 timeout_in_minutes: 30 inputs: [test/cluster] @@ -306,7 +308,7 @@ steps: queue: hetzner-aarch64-16cpu-32gb - id: sqllogictest-fast - label: "Fast SQL logic tests" + label: SLT depends_on: build-aarch64 timeout_in_minutes: 30 inputs: [test/sqllogictest] @@ -320,7 +322,7 @@ steps: queue: hetzner-aarch64-16cpu-32gb - id: restarts - label: "Restart test" + label: Restart depends_on: build-aarch64 timeout_in_minutes: 30 parallelism: 4 @@ -330,11 +332,11 @@ steps: agents: queue: hetzner-aarch64-8cpu-16gb - - group: "MySQL tests" + - group: "MySQL" key: mysql-tests steps: - id: mysql-cdc - label: "MySQL CDC tests" + label: MySQL CDC parallelism: 8 depends_on: build-aarch64 timeout_in_minutes: 30 @@ -346,7 +348,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: mysql-rtr - label: "MySQL RTR tests" + label: MySQL RTR depends_on: build-aarch64 timeout_in_minutes: 30 inputs: [test/mysql-rtr] @@ -356,11 +358,11 @@ steps: agents: queue: hetzner-aarch64-4cpu-8gb - - group: "Postgres tests" + - group: Postgres key: postgres-tests steps: - id: pg-cdc - label: "Postgres CDC tests" + label: Postgres CDC parallelism: 8 depends_on: build-aarch64 timeout_in_minutes: 30 @@ -373,7 +375,7 @@ steps: # the mzbuild postgres version will be used, which depends on the Dockerfile specification - id: pg-rtr - label: "Postgres RTR tests" + label: Postgres RTR depends_on: build-aarch64 timeout_in_minutes: 30 inputs: [test/pg-rtr] @@ -384,7 +386,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: sql-server-cdc - label: "SQL Server CDC tests" + label: SQL Server depends_on: build-x86_64 timeout_in_minutes: 30 inputs: [test/sql-server-cdc] @@ -398,11 +400,11 @@ steps: # See: queue: hetzner-x86-64-8cpu-16gb - - group: "Connection tests" + - group: Connection key: connection-tests steps: - id: ssh-connection - label: SSH connection tests + label: SSH connection depends_on: build-x86_64 timeout_in_minutes: 40 inputs: [test/ssh-connection] @@ -414,7 +416,7 @@ steps: queue: hetzner-x86-64-8cpu-16gb - id: fivetran-destination-tests - label: Fivetran Destination tests + label: Fivetran Destination depends_on: build-aarch64 timeout_in_minutes: 30 inputs: [test/fivetran-destination] @@ -424,11 +426,11 @@ steps: agents: queue: hetzner-aarch64-4cpu-8gb - - group: "Kafka tests" + - group: Kafka key: kafka-tests steps: - id: kafka-auth - label: Kafka auth test + label: Kafka auth depends_on: build-aarch64 timeout_in_minutes: 30 inputs: [test/kafka-auth] @@ -440,7 +442,7 @@ steps: queue: hetzner-aarch64-8cpu-16gb - id: kafka-exactly-once - label: Kafka exactly-once tests + label: Kafka exactly-once depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -450,7 +452,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: kafka-rtr - label: "Kafka RTR tests" + label: Kafka RTR depends_on: build-aarch64 timeout_in_minutes: 30 artifact_paths: junit_*.xml @@ -473,11 +475,11 @@ steps: args: [--scenario=KafkaSources, --actions=80] - id: checks-no-restart-no-upgrade - label: "Checks parallel without restart or upgrade" + label: Checks parallel depends_on: build-x86_64 inputs: [misc/python/materialize/checks] timeout_in_minutes: 45 - parallelism: 16 + parallelism: 8 agents: # Uses SQL Server, which is not yet available for ARM queue: hetzner-x86-64-8cpu-16gb @@ -494,7 +496,7 @@ steps: ] - id: source-sink-errors - label: "Source/Sink Error Reporting" + label: "S&S error reporting" depends_on: build-aarch64 parallelism: 3 timeout_in_minutes: 30 @@ -507,7 +509,7 @@ steps: # Fast tests closer to the end, doesn't matter as much if they have to wait # for an agent - id: persistence - label: Persistence tests + label: Persistence depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -517,7 +519,7 @@ steps: queue: hetzner-aarch64-8cpu-16gb - id: cluster-isolation - label: Cluster isolation test + label: Cluster isolation depends_on: build-aarch64 timeout_in_minutes: 20 inputs: [test/cluster-isolation] @@ -528,7 +530,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: dbt-materialize - label: dbt-materialize tests + label: dbt-materialize depends_on: build-aarch64 timeout_in_minutes: 30 parallelism: 2 @@ -538,23 +540,11 @@ steps: agents: queue: hetzner-aarch64-8cpu-16gb - - id: dataflow-visualizer - label: "Dataflow Visualizer E2E tests" - depends_on: build-x86_64 - timeout_in_minutes: 30 - inputs: [test/dataflow-visualizer] - plugins: - - ./ci/plugins/mzcompose: - composition: dataflow-visualizer - agents: - # Playwright Docker image is x86_64 only - queue: hetzner-x86-64-4cpu-8gb - - - group: "Debezium tests" + - group: Debezium key: debezium-tests steps: - id: debezium-postgres - label: "Debezium Postgres tests" + label: Debezium Postgres depends_on: build-aarch64 timeout_in_minutes: 30 inputs: [test/debezium] @@ -566,7 +556,7 @@ steps: queue: hetzner-aarch64-8cpu-16gb - id: debezium-sql-server - label: "Debezium SQL Server tests" + label: Debezium SQL Server depends_on: build-x86_64 timeout_in_minutes: 30 inputs: [test/debezium] @@ -579,7 +569,7 @@ steps: queue: hetzner-x86-64-4cpu-8gb - id: debezium-mysql - label: "Debezium MySQL tests" + label: Debezium MySQL depends_on: build-aarch64 timeout_in_minutes: 30 inputs: [test/debezium] @@ -591,7 +581,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: storage-usage - label: "Storage Usage Table Test" + label: Storage usage table depends_on: build-aarch64 timeout_in_minutes: 30 agents: @@ -601,7 +591,7 @@ steps: composition: storage-usage - id: tracing - label: "Tracing Fast Path" + label: Tracing fast path depends_on: build-aarch64 timeout_in_minutes: 30 inputs: [test/tracing] @@ -680,7 +670,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: mcp-materialize - label: mcp-materialize tests + label: mcp-materialize depends_on: build-aarch64 timeout_in_minutes: 30 plugins: @@ -690,7 +680,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: chbench-demo - label: chbench smoke test + label: chbench smoke depends_on: build-aarch64 plugins: - ./ci/plugins/mzcompose: @@ -702,7 +692,7 @@ steps: queue: hetzner-aarch64-4cpu-8gb - id: metabase-demo - label: Metabase smoke test + label: Metabase depends_on: build-x86_64 timeout_in_minutes: 30 plugins: @@ -712,9 +702,19 @@ steps: # too slow to run emulated on aarch64, Metabase'ss docker image is not yet available for aarch64 natively yet: https://github.com/metabase/metabase/issues/13119 queue: hetzner-x86-64-4cpu-8gb - - group: Docs tests + - id: fdb + label: "FoundationDB" + depends_on: build-aarch64 + timeout_in_minutes: 40 + inputs: [test/foundationdb] + plugins: + - ./ci/plugins/mzcompose: + composition: foundationdb + agents: + queue: hetzner-aarch64-4cpu-8gb + + - group: Docs key: docs-tests - label: ":rust: Docs tests" steps: - id: lint-docs label: Lint docs @@ -769,22 +769,9 @@ steps: # SQL Server doesn't support ARM: https://github.com/microsoft/mssql-docker/issues/944 queue: hetzner-x86-64-4cpu-8gb - - group: Console tests + - group: Console key: console-tests steps: - - id: console-lint - label: "Console lint and typecheck" - command: bin/ci-builder run stable ci/test/console/lint.sh - inputs: - - console/** - - ci/test/console/** - depends_on: [] - timeout_in_minutes: 15 - agents: - queue: hetzner-aarch64-4cpu-8gb - coverage: skip - sanitizer: skip - - id: console-test label: "Console unit tests" command: bin/ci-builder run stable ci/test/console/test.sh @@ -800,7 +787,7 @@ steps: sanitizer: skip - id: console-sql-test - label: "Console SQL tests" + label: "Console SQL" depends_on: build-x86_64 timeout_in_minutes: 30 inputs: @@ -817,7 +804,7 @@ steps: sanitizer: skip - id: console-e2e-test - label: "Console E2E against staging" + label: "Console E2E/staging" command: bin/ci-builder run console ci/test/console/e2e.sh inputs: - console/** @@ -836,7 +823,7 @@ steps: ci_glue_exempt: true - id: console-e2e-test-prod - label: "Console E2E against production" + label: "Console E2E/prod" command: bin/ci-builder run console ci/test/console/e2e-prod.sh inputs: - console/** @@ -869,6 +856,18 @@ steps: sanitizer: skip ci_glue_exempt: true + - id: dataflow-visualizer + label: Dataflow visualizer + depends_on: build-x86_64 + timeout_in_minutes: 30 + inputs: [test/dataflow-visualizer] + plugins: + - ./ci/plugins/mzcompose: + composition: dataflow-visualizer + agents: + # Playwright Docker image is x86_64 only + queue: hetzner-x86-64-4cpu-8gb + - id: deploy-website label: Deploy website depends_on: lint-docs From 9b5a53ef89785299949e1c8e135322428c17464d Mon Sep 17 00:00:00 2001 From: Dennis Felsing Date: Thu, 26 Feb 2026 17:03:57 +0000 Subject: [PATCH 3/3] add vercel-preview status update --- ci/test/console/vercel-preview.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/ci/test/console/vercel-preview.sh b/ci/test/console/vercel-preview.sh index 641cba3b31312..af4bef9d52b65 100755 --- a/ci/test/console/vercel-preview.sh +++ b/ci/test/console/vercel-preview.sh @@ -26,3 +26,14 @@ alias_url="$(node bin/vercel-preview-url.js "${BUILDKITE_BRANCH}")" echo "Aliasing $deployment_url to $alias_url" npx vercel@latest alias --scope=materialize --token="$VERCEL_TOKEN" set "$deployment_url" "$alias_url" printf "+++ Console preview: \033]1339;url='https://%s'\a\n" "$alias_url" + +curl -fsSL \ + -H "Authorization: Bearer $GITHUB_TOKEN" \ + -H "Accept: application/vnd.github.v3+json" \ + "https://api.github.com/repos/MaterializeInc/materialize/statuses/$BUILDKITE_COMMIT" \ + --data "{\ + \"state\": \"success\",\ + \"description\": \"Console preview ready.\",\ + \"target_url\": \"https://$alias_url\",\ + \"context\": \"console-preview\"\ + }"