From 5f9740bc864869863e679ff6c275d5f97bdac6f8 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Sun, 16 Nov 2025 10:33:45 +0000 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=A4=96=20ci:=20add=20smoke=20test=20s?= =?UTF-8?q?uite=20for=20npm=20package?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add comprehensive smoke test suite that validates: - npm package installation from tarball - server startup with 'mux server' command - healthcheck endpoint returns valid JSON response - root endpoint is accessible The smoke test runs on merge to main and can also be run locally with: make smoke-test Test includes: - Proper cleanup of server processes and temp directories - Configurable timeouts for startup and healthcheck - Colored output for better visibility - Server logs uploaded as artifacts on failure _Generated with `mux`_ --- .github/workflows/smoke-test.yml | 59 +++++++++++ Makefile | 13 ++- scripts/smoke-test.sh | 173 +++++++++++++++++++++++++++++++ 3 files changed, 244 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/smoke-test.yml create mode 100755 scripts/smoke-test.sh diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml new file mode 100644 index 000000000..892f101a2 --- /dev/null +++ b/.github/workflows/smoke-test.yml @@ -0,0 +1,59 @@ +name: Smoke Test + +on: + push: + branches: + - main + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + smoke-test: + name: NPM Package Smoke Test + runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }} + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 # Required for git describe to find tags + + - uses: ./.github/actions/setup-mux + + - name: Generate version file + run: ./scripts/generate-version.sh + + - name: Build the project + run: make build + + - name: Pack npm package + run: npm pack + id: pack + + - name: Find package tarball + id: find-tarball + run: | + TARBALL=$(ls mux-*.tgz | head -1) + echo "tarball=$TARBALL" >> $GITHUB_OUTPUT + echo "Found package: $TARBALL" + + - name: Run smoke test + env: + PACKAGE_TARBALL: ${{ steps.find-tarball.outputs.tarball }} + SERVER_PORT: 3000 + SERVER_HOST: localhost + STARTUP_TIMEOUT: 30 + run: | + ./scripts/smoke-test.sh + + - name: Upload server logs on failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: smoke-test-logs + path: | + /tmp/mux-smoke-test-*/server.log + if-no-files-found: warn + retention-days: 7 diff --git a/Makefile b/Makefile index 339f8190b..0628673f9 100644 --- a/Makefile +++ b/Makefile @@ -46,7 +46,7 @@ include fmt.mk .PHONY: all build dev start clean help .PHONY: build-renderer version build-icons build-static .PHONY: lint lint-fix typecheck static-check -.PHONY: test test-unit test-integration test-watch test-coverage test-e2e +.PHONY: test test-unit test-integration test-watch test-coverage test-e2e smoke-test .PHONY: dist dist-mac dist-win dist-linux .PHONY: vscode-ext vscode-ext-install .PHONY: docs docs-build docs-watch @@ -255,6 +255,17 @@ test-watch: ## Run tests in watch mode test-coverage: ## Run tests with coverage @./scripts/test.sh --coverage + +smoke-test: build ## Run smoke test on npm package + @echo "Building npm package tarball..." + @npm pack + @TARBALL=$$(ls mux-*.tgz | head -1); \ + echo "Running smoke test on $$TARBALL..."; \ + PACKAGE_TARBALL="$$TARBALL" ./scripts/smoke-test.sh; \ + EXIT_CODE=$$?; \ + rm -f "$$TARBALL"; \ + exit $$EXIT_CODE + test-e2e: ## Run end-to-end tests @$(MAKE) build @MUX_E2E_LOAD_DIST=1 MUX_E2E_SKIP_BUILD=1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 bun x playwright test --project=electron $(PLAYWRIGHT_ARGS) diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh new file mode 100755 index 000000000..f7424f7df --- /dev/null +++ b/scripts/smoke-test.sh @@ -0,0 +1,173 @@ +#!/usr/bin/env bash +# Smoke test for mux npm package +# Tests that the package can be installed and the server starts correctly + +set -euo pipefail + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Logging functions +log_info() { + echo -e "${GREEN}[INFO]${NC} $*" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $*" >&2 +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $*" +} + +# Cleanup function +cleanup() { + local exit_code=$? + log_info "Cleaning up..." + + # Kill server if it's running + if [[ -n "${SERVER_PID:-}" ]] && kill -0 "$SERVER_PID" 2>/dev/null; then + log_info "Stopping server (PID: $SERVER_PID)..." + kill "$SERVER_PID" 2>/dev/null || true + wait "$SERVER_PID" 2>/dev/null || true + fi + + # Remove test directory + if [[ -n "${TEST_DIR:-}" ]] && [[ -d "$TEST_DIR" ]]; then + log_info "Removing test directory: $TEST_DIR" + rm -rf "$TEST_DIR" + fi + + if [[ $exit_code -eq 0 ]]; then + log_info "✅ Smoke test completed successfully" + else + log_error "❌ Smoke test failed with exit code $exit_code" + fi + + exit $exit_code +} + +trap cleanup EXIT INT TERM + +# Configuration +PACKAGE_TARBALL="${PACKAGE_TARBALL:-}" +SERVER_PORT="${SERVER_PORT:-3000}" +SERVER_HOST="${SERVER_HOST:-localhost}" +STARTUP_TIMEOUT="${STARTUP_TIMEOUT:-30}" +HEALTHCHECK_TIMEOUT="${HEALTHCHECK_TIMEOUT:-10}" + +# Validate required arguments +if [[ -z "$PACKAGE_TARBALL" ]]; then + log_error "PACKAGE_TARBALL environment variable must be set" + log_error "Usage: PACKAGE_TARBALL=/path/to/package.tgz $0" + exit 1 +fi + +if [[ ! -f "$PACKAGE_TARBALL" ]]; then + log_error "Package tarball not found: $PACKAGE_TARBALL" + exit 1 +fi + +log_info "Starting smoke test for package: $PACKAGE_TARBALL" + +# Create temporary test directory +TEST_DIR=$(mktemp -d) +log_info "Created test directory: $TEST_DIR" + +cd "$TEST_DIR" + +# Initialize a minimal package.json to avoid npm warnings +cat >package.json <server.log 2>&1 & +SERVER_PID=$! + +log_info "Server started with PID: $SERVER_PID" + +# Wait for server to start +log_info "Waiting for server to start (timeout: ${STARTUP_TIMEOUT}s)..." +ELAPSED=0 +while [[ $ELAPSED -lt $STARTUP_TIMEOUT ]]; do + if ! kill -0 "$SERVER_PID" 2>/dev/null; then + log_error "Server process died unexpectedly" + log_error "Server log:" + cat server.log + exit 1 + fi + + # Try to connect to the server + if curl -sf "http://${SERVER_HOST}:${SERVER_PORT}/health" >/dev/null 2>&1; then + log_info "✅ Server is responding" + break + fi + + sleep 1 + ELAPSED=$((ELAPSED + 1)) +done + +if [[ $ELAPSED -ge $STARTUP_TIMEOUT ]]; then + log_error "Server failed to start within ${STARTUP_TIMEOUT}s" + log_error "Server log:" + cat server.log + exit 1 +fi + +# Test healthcheck endpoint +log_info "Testing healthcheck endpoint..." +HEALTH_RESPONSE=$(curl -sf "http://${SERVER_HOST}:${SERVER_PORT}/health" || true) + +if [[ -z "$HEALTH_RESPONSE" ]]; then + log_error "Healthcheck returned empty response" + exit 1 +fi + +log_info "Healthcheck response: $HEALTH_RESPONSE" + +# Verify healthcheck response format +if ! echo "$HEALTH_RESPONSE" | jq -e '.status == "ok"' >/dev/null 2>&1; then + log_error "Healthcheck response does not contain expected 'status: ok'" + log_error "Response: $HEALTH_RESPONSE" + exit 1 +fi + +log_info "✅ Healthcheck endpoint returned valid response" + +# Test that server is actually serving content +log_info "Testing root endpoint..." +if ! curl -sf "http://${SERVER_HOST}:${SERVER_PORT}/" >/dev/null 2>&1; then + log_error "Failed to fetch root endpoint" + exit 1 +fi + +log_info "✅ Root endpoint is accessible" + +# All tests passed +log_info "🎉 All smoke tests passed!" From 930d5f4e44bcd7f11f2bf6baff0e7b5775974ad5 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Sun, 16 Nov 2025 10:40:30 +0000 Subject: [PATCH 2/3] fix: convert tarball path to absolute and run on PRs - Convert PACKAGE_TARBALL to absolute path before cd to avoid path resolution issues - Add pull_request trigger to smoke-test workflow so it runs on PRs Addresses Codex feedback about tarball path being relative after cd. _Generated with `mux`_ --- .github/workflows/smoke-test.yml | 2 ++ scripts/smoke-test.sh | 3 +++ 2 files changed, 5 insertions(+) diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml index 892f101a2..7750afa87 100644 --- a/.github/workflows/smoke-test.yml +++ b/.github/workflows/smoke-test.yml @@ -1,6 +1,8 @@ name: Smoke Test on: + pull_request: + branches: ["**"] push: branches: - main diff --git a/scripts/smoke-test.sh b/scripts/smoke-test.sh index f7424f7df..ce5c6a22b 100755 --- a/scripts/smoke-test.sh +++ b/scripts/smoke-test.sh @@ -71,6 +71,9 @@ if [[ ! -f "$PACKAGE_TARBALL" ]]; then exit 1 fi +# Convert to absolute path before changing directories +PACKAGE_TARBALL=$(realpath "$PACKAGE_TARBALL") + log_info "Starting smoke test for package: $PACKAGE_TARBALL" # Create temporary test directory From e4a8907ce4ccbb2e99a0af680651172053c86638 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Sun, 16 Nov 2025 17:11:53 +0000 Subject: [PATCH 3/3] refactor: move smoke test to npm publish workflow - Move smoke test from standalone workflow to publish-npm.yml - Run smoke test after build, before npm publish - Remove smoke-test.yml standalone workflow - Smoke test now acts as gate before publishing to npm This ensures packages are validated before publishing rather than after merge to main. _Generated with `mux`_ --- .github/workflows/publish-npm.yml | 33 +++++++++++++++++ .github/workflows/smoke-test.yml | 61 ------------------------------- 2 files changed, 33 insertions(+), 61 deletions(-) delete mode 100644 .github/workflows/smoke-test.yml diff --git a/.github/workflows/publish-npm.yml b/.github/workflows/publish-npm.yml index c0e60396a..4d31eabbc 100644 --- a/.github/workflows/publish-npm.yml +++ b/.github/workflows/publish-npm.yml @@ -69,6 +69,39 @@ jobs: - name: Build application run: make build + - name: Pack npm package for smoke test + run: npm pack + + - name: Find package tarball + id: find-tarball + run: | + TARBALL=$(ls mux-*.tgz | head -1) + echo "tarball=$TARBALL" >> $GITHUB_OUTPUT + echo "Found package: $TARBALL" + + - name: Run smoke test + env: + PACKAGE_TARBALL: ${{ steps.find-tarball.outputs.tarball }} + SERVER_PORT: 3000 + SERVER_HOST: localhost + STARTUP_TIMEOUT: 30 + run: | + ./scripts/smoke-test.sh + + - name: Upload server logs on smoke test failure + if: failure() + uses: actions/upload-artifact@v4 + with: + name: smoke-test-logs + path: | + /tmp/mux-smoke-test-*/server.log + if-no-files-found: warn + retention-days: 7 + + - name: Clean up tarball + if: always() + run: rm -f mux-*.tgz + - name: Check if version exists id: check-exists run: | diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml deleted file mode 100644 index 7750afa87..000000000 --- a/.github/workflows/smoke-test.yml +++ /dev/null @@ -1,61 +0,0 @@ -name: Smoke Test - -on: - pull_request: - branches: ["**"] - push: - branches: - - main - workflow_dispatch: - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - smoke-test: - name: NPM Package Smoke Test - runs-on: ${{ github.repository_owner == 'coder' && 'depot-ubuntu-22.04-16' || 'ubuntu-latest' }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 # Required for git describe to find tags - - - uses: ./.github/actions/setup-mux - - - name: Generate version file - run: ./scripts/generate-version.sh - - - name: Build the project - run: make build - - - name: Pack npm package - run: npm pack - id: pack - - - name: Find package tarball - id: find-tarball - run: | - TARBALL=$(ls mux-*.tgz | head -1) - echo "tarball=$TARBALL" >> $GITHUB_OUTPUT - echo "Found package: $TARBALL" - - - name: Run smoke test - env: - PACKAGE_TARBALL: ${{ steps.find-tarball.outputs.tarball }} - SERVER_PORT: 3000 - SERVER_HOST: localhost - STARTUP_TIMEOUT: 30 - run: | - ./scripts/smoke-test.sh - - - name: Upload server logs on failure - if: failure() - uses: actions/upload-artifact@v4 - with: - name: smoke-test-logs - path: | - /tmp/mux-smoke-test-*/server.log - if-no-files-found: warn - retention-days: 7