diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 000000000..da8d48ee3 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,205 @@ +name: Docker Pull and Build + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + docker-compose-pull: + name: Docker Compose Pull Test + runs-on: ubuntu-latest + + services: + docker: + image: docker:26.0.0 + options: --privileged + + steps: + - name: Get repository and branch information + id: repo-info + run: | + if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.pull_request.head.repo.fork }}" = "true" ]; then + echo "REPO_FULL_NAME=lanedirt/AliasVault" >> $GITHUB_ENV + echo "BRANCH_NAME=main" >> $GITHUB_ENV + else + echo "REPO_FULL_NAME=${GITHUB_REPOSITORY}" >> $GITHUB_ENV + echo "BRANCH_NAME=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_ENV + fi + + - name: Download install script from current branch + run: | + INSTALL_SCRIPT_URL="https://raw.githubusercontent.com/$REPO_FULL_NAME/$BRANCH_NAME/install.sh" + echo "Downloading install script from: $INSTALL_SCRIPT_URL" + curl -f -o install.sh "$INSTALL_SCRIPT_URL" + + - name: Create .env file with custom SMTP port + run: echo "SMTP_PORT=2525" > .env + + - name: Set permissions and run install.sh (install) + id: install_script + run: | + chmod +x install.sh + { + ./install.sh install --verbose + exit_code=$? + if [ $exit_code -eq 2 ]; then + echo "skip_remaining=true" >> $GITHUB_OUTPUT + true + elif [ $exit_code -ne 0 ]; then + false + fi + } || { + if [ $exit_code -eq 2 ]; then + echo "skip_remaining=true" >> $GITHUB_OUTPUT + true + else + exit $exit_code + fi + } + + - name: Run docker compose up + if: ${{ !steps.install_script.outputs.skip_remaining }} + run: docker compose -f docker-compose.yml up -d + + - name: Wait for services + if: ${{ !steps.install_script.outputs.skip_remaining }} + run: sleep 10 + + - name: Test WASM App + if: ${{ !steps.install_script.outputs.skip_remaining }} + uses: nick-fields/retry@v3 + with: + timeout_minutes: 2 + max_attempts: 3 + command: | + http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443) + if [ "$http_code" -ne 200 ]; then + echo "WASM app failed with $http_code" + exit 1 + fi + + - name: Test WebApi + if: ${{ !steps.install_script.outputs.skip_remaining }} + uses: nick-fields/retry@v3 + with: + timeout_minutes: 2 + max_attempts: 3 + command: | + http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443/api) + if [ "$http_code" -ne 200 ]; then + echo "WebApi failed with $http_code" + exit 1 + fi + + - name: Test Admin App + if: ${{ !steps.install_script.outputs.skip_remaining }} + uses: nick-fields/retry@v3 + with: + timeout_minutes: 2 + max_attempts: 3 + command: | + http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443/admin/user/login) + if [ "$http_code" -ne 200 ]; then + echo "Admin app failed with $http_code" + exit 1 + fi + + - name: Test SMTP + if: ${{ !steps.install_script.outputs.skip_remaining }} + uses: nick-fields/retry@v3 + with: + timeout_minutes: 2 + max_attempts: 3 + command: | + if ! nc -zv localhost 2525 2>&1 | grep -q 'succeeded'; then + echo "SMTP failed" + exit 1 + fi + + - name: Test reset-admin-password output + if: ${{ !steps.install_script.outputs.skip_remaining }} + run: | + output=$(./install.sh reset-admin-password) + if ! echo "$output" | grep -E '.*New admin password: [A-Za-z0-9+/=]{8,}.*'; then + echo "Invalid reset-admin-password output" + exit 1 + fi + + docker-compose-build: + name: Docker Compose Build Test + runs-on: ubuntu-latest + + services: + docker: + image: docker:26.0.0 + options: --privileged + + steps: + - uses: actions/checkout@v2 + + - name: Create .env file with custom SMTP port + run: echo "SMTP_PORT=2525" > .env + + - name: Set permissions and run install.sh build + run: | + chmod +x install.sh + ./install.sh build --verbose + + - name: Test services are responding + uses: nick-fields/retry@v3 + with: + timeout_minutes: 5 + max_attempts: 5 + command: | + sleep 15 + declare -A endpoints=( + ["WASM"]="https://localhost:443" + ["WebApi"]="https://localhost:443/api" + ["Admin"]="https://localhost:443/admin/user/login" + ) + failed=false + for name in "${!endpoints[@]}"; do + url="${endpoints[$name]}" + echo "Testing $name at $url" + response=$(curl -k -s -w "\nHTTP_CODE=%{http_code}" "$url") + http_code=$(echo "$response" | grep "HTTP_CODE=" | cut -d= -f2) + if [ "$http_code" -ne 200 ]; then + echo "❌ $name failed with $http_code" + failed=true + else + echo "✅ $name passed" + fi + done + + echo "Testing SMTP on port 2525" + if ! nc -zv localhost 2525 2>&1 | grep -q 'succeeded'; then + echo "❌ SMTP failed" + failed=true + else + echo "✅ SMTP passed" + fi + + if [ "$failed" = true ]; then + echo "Dumping logs" + docker compose logs admin + docker compose logs api + docker compose logs client + docker compose logs smtp + docker compose logs reverse-proxy + docker compose restart + exit 1 + fi + + - name: Test reset-admin-password output + run: | + output=$(./install.sh reset-admin-password) + if ! echo "$output" | grep -E '.*New admin password: [A-Za-z0-9+/=]{8,}.*'; then + echo "Invalid reset-admin-password output" + exit 1 + fi diff --git a/.github/workflows/docker-compose-build.yml b/.github/workflows/docker-compose-build.yml deleted file mode 100644 index c694d71d6..000000000 --- a/.github/workflows/docker-compose-build.yml +++ /dev/null @@ -1,107 +0,0 @@ -name: Docker Compose Build - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test-docker: - runs-on: ubuntu-latest - - services: - docker: - image: docker:26.0.0 - options: --privileged - - steps: - - uses: actions/checkout@v2 - - - name: Create .env file with custom SMTP port as port 25 is not allowed in GitHub Actions - run: | - echo "SMTP_PORT=2525" > .env - - - name: Set permissions and run install.sh - run: | - chmod +x install.sh - ./install.sh build --verbose - - - name: Test if services are responding - uses: nick-fields/retry@v3 - with: - timeout_minutes: 5 - max_attempts: 5 - command: | - sleep 15 - - # Array of endpoints to test - declare -A endpoints=( - ["WASM"]="https://localhost:443" - ["WebApi"]="https://localhost:443/api" - ["Admin"]="https://localhost:443/admin/user/login" - ) - - failed=false - - # Test HTTP endpoints - for name in "${!endpoints[@]}"; do - url="${endpoints[$name]}" - echo "Testing $name at $url" - - # Store both response body and HTTP code - response=$(curl -k -s -w "\nHTTP_CODE=%{http_code}" "$url") - http_code=$(echo "$response" | grep "HTTP_CODE=" | cut -d= -f2) - body=$(echo "$response" | sed '$d') # Remove the last line (HTTP_CODE) - - if [ "$http_code" -ne 200 ]; then - echo "❌ $name failed with HTTP $http_code at $url" - echo "Response body:" - echo "$body" - failed=true - else - echo "✅ $name responded with HTTP 200" - fi - done - - # Test SMTP - echo "Testing SmtpService at localhost:2525" - if ! nc -zv localhost 2525 2>&1 | grep -q 'succeeded'; then - echo "❌ SmtpService failed to respond on port 2525" - failed=true - else - echo "✅ SmtpService responded successfully" - fi - - # Exit with error if any service failed - if [ "$failed" = true ]; then - # Get container logs - echo "Container Logs admin:" - docker compose logs admin - echo "Container Logs api:" - docker compose logs api - echo "Container Logs client:" - docker compose logs client - echo "Container Logs smtp:" - docker compose logs smtp - echo "Container Logs reverse-proxy:" - docker compose logs reverse-proxy - - # Restart containers for next test in case of failure - docker compose restart - exit 1 - fi - - - name: Test install.sh reset-admin-password output - run: | - output=$(./install.sh reset-admin-password) - if ! echo "$output" | grep -E '.*New admin password: [A-Za-z0-9+/=]{8,}.*'; then - echo "Password reset output format is incorrect" - echo "Expected: 'New admin password: '" - echo "Actual: $output" - exit 1 - fi diff --git a/.github/workflows/docker-compose-pull.yml b/.github/workflows/docker-compose-pull.yml deleted file mode 100644 index 263e50bc2..000000000 --- a/.github/workflows/docker-compose-pull.yml +++ /dev/null @@ -1,150 +0,0 @@ -# This workflow will test if pulling the latest Docker Compose containers from the registry works. -name: Docker Compose Pull - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test-docker: - runs-on: ubuntu-latest - - services: - docker: - image: docker:26.0.0 - options: --privileged - - steps: - - name: Get repository and branch information - id: repo-info - run: | - # Check if this is a PR from a fork - if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ github.event.pull_request.head.repo.fork }}" = "true" ]; then - # If PR is from a fork, use main branch from lanedirt/AliasVault - echo "REPO_FULL_NAME=lanedirt/AliasVault" >> $GITHUB_ENV - echo "BRANCH_NAME=main" >> $GITHUB_ENV - else - # Otherwise use the current repository and branch - echo "REPO_FULL_NAME=${GITHUB_REPOSITORY}" >> $GITHUB_ENV - echo "BRANCH_NAME=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_ENV - fi - - - name: Download install script from current branch - run: | - INSTALL_SCRIPT_URL="https://raw.githubusercontent.com/$REPO_FULL_NAME/$BRANCH_NAME/install.sh" - echo "Downloading install script from: $INSTALL_SCRIPT_URL" - curl -f -o install.sh "$INSTALL_SCRIPT_URL" - - - name: Create .env file with custom SMTP port as port 25 is not allowed in GitHub Actions - run: | - echo "SMTP_PORT=2525" > .env - - - name: Set permissions and run install.sh - id: install_script - run: | - chmod +x install.sh - { - ./install.sh install --verbose - exit_code=$? - if [ $exit_code -eq 2 ]; then - echo "Test skipped: Install script version is newer than latest release version. This is expected behavior if the install script is run on a branch that is ahead of the latest release." - echo "skip_remaining=true" >> $GITHUB_OUTPUT - true # Force success exit code - elif [ $exit_code -ne 0 ]; then - false # Propagate failure - fi - } || { - if [ $exit_code -eq 2 ]; then - echo "skip_remaining=true" >> $GITHUB_OUTPUT - true # Version mismatch is okay - else - exit $exit_code # Propagate other failures - fi - } - - - name: Set up Docker Compose - if: ${{ !steps.install_script.outputs.skip_remaining }} - run: docker compose -f docker-compose.yml up -d - - - name: Wait for services to be up - if: ${{ !steps.install_script.outputs.skip_remaining }} - run: | - # Wait for a few seconds - sleep 10 - - - name: Test if localhost:443 (WASM app) responds - if: ${{ !steps.install_script.outputs.skip_remaining }} - uses: nick-fields/retry@v3 - with: - timeout_minutes: 2 - max_attempts: 3 - command: | - http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443) - if [ "$http_code" -ne 200 ]; then - echo "Service did not respond with 200 OK. Check if client app and/or nginx is configured correctly." - exit 1 - else - echo "Service responded with 200 OK" - fi - - - name: Test if localhost:443/api (WebApi) responds - if: ${{ !steps.install_script.outputs.skip_remaining }} - uses: nick-fields/retry@v3 - with: - timeout_minutes: 2 - max_attempts: 3 - command: | - http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443/api) - if [ "$http_code" -ne 200 ]; then - echo "Service did not respond with expected 200 OK. Check if WebApi and/or nginx is configured correctly." - exit 1 - else - echo "Service responded with $http_code" - fi - - - name: Test if localhost:443/admin (Admin) responds - if: ${{ !steps.install_script.outputs.skip_remaining }} - uses: nick-fields/retry@v3 - with: - timeout_minutes: 2 - max_attempts: 3 - command: | - http_code=$(curl -k -s -o /dev/null -w "%{http_code}" https://localhost:443/admin/user/login) - if [ "$http_code" -ne 200 ]; then - echo "Service did not respond with expected 200 OK. Check if admin app and/or nginx is configured correctly." - exit 1 - else - echo "Service responded with $http_code" - fi - - - name: Test if localhost:2525 (SmtpService) responds - if: ${{ !steps.install_script.outputs.skip_remaining }} - uses: nick-fields/retry@v3 - with: - timeout_minutes: 2 - max_attempts: 3 - command: | - if ! nc -zv localhost 2525 2>&1 | grep -q 'succeeded'; then - echo "SmtpService did not respond on port 2525. Check if the SmtpService service is running." - exit 1 - else - echo "SmtpService responded on port 2525" - fi - - - name: Test install.sh reset-admin-password output - if: ${{ !steps.install_script.outputs.skip_remaining }} - run: | - output=$(./install.sh reset-admin-password) - if ! echo "$output" | grep -E '.*New admin password: [A-Za-z0-9+/=]{8,}.*'; then - echo "Password reset output format is incorrect. Expected format: 'New admin password: '" - echo "Actual output: $output" - exit 1 - else - echo "Password reset output format is correct" - fi diff --git a/.github/workflows/dotnet-e2e-admin-tests.yml b/.github/workflows/dotnet-e2e-admin-tests.yml deleted file mode 100644 index 38988c5c8..000000000 --- a/.github/workflows/dotnet-e2e-admin-tests.yml +++ /dev/null @@ -1,50 +0,0 @@ -# This workflow will test if running the E2E Admin tests via Playwright CLI works. -name: .NET E2E Admin Tests (Playwright) - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - admin-tests: - timeout-minutes: 60 - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 9.0.x - - - name: Install dependencies - run: dotnet workload install wasm-tools - - - name: Build - run: dotnet build - - - name: Start dev database - run: ./install.sh configure-dev-db start - - - name: Ensure browsers are installed - run: pwsh apps/server/Tests/AliasVault.E2ETests/bin/Debug/net9.0/playwright.ps1 install --with-deps - - - name: Run AdminTests with retry - uses: nick-fields/retry@v3 - with: - timeout_minutes: 60 - max_attempts: 3 - command: dotnet test apps/server/Tests/AliasVault.E2ETests --no-build --verbosity normal --filter "Category=AdminTests" - - - name: Upload Test Results - if: always() - uses: actions/upload-artifact@v4 - with: - name: admin-test-results - path: TestResults-Admin.xml diff --git a/.github/workflows/dotnet-e2e-client-tests.yml b/.github/workflows/dotnet-e2e-client-tests.yml deleted file mode 100644 index d2001b30a..000000000 --- a/.github/workflows/dotnet-e2e-client-tests.yml +++ /dev/null @@ -1,47 +0,0 @@ -# This workflow will test if running the E2E Client tests via Playwright CLI works. -name: .NET E2E Client Tests (Playwright with Sharding) - -on: - push: - branches: [ "main" ] - pull_request: - branches: [ "main" ] - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - client-tests: - timeout-minutes: 60 - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - shard: [1, 2, 3, 4, 5] - steps: - - uses: actions/checkout@v4 - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 9.0.x - - - name: Install dependencies - run: dotnet workload install wasm-tools - - - name: Build - run: dotnet build - - - name: Start dev database - run: ./install.sh configure-dev-db start - - - name: Ensure browsers are installed - run: pwsh apps/server/Tests/AliasVault.E2ETests/bin/Debug/net9.0/playwright.ps1 install --with-deps - - - name: Run ClientTests with retry (Shard ${{ matrix.shard }}) - uses: nick-fields/retry@v3 - with: - timeout_minutes: 60 - max_attempts: 3 - command: dotnet test apps/server/Tests/AliasVault.E2ETests --no-build --verbosity normal --filter "FullyQualifiedName~.E2ETests.Tests.Client.Shard${{ matrix.shard }}." diff --git a/.github/workflows/dotnet-e2e-tests.yml b/.github/workflows/dotnet-e2e-tests.yml new file mode 100644 index 000000000..a4fb6d600 --- /dev/null +++ b/.github/workflows/dotnet-e2e-tests.yml @@ -0,0 +1,83 @@ +name: .NET E2E Tests (with Sharding) + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + admin-tests: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 9.0.x + + - name: Install dependencies + run: dotnet workload install wasm-tools + + - name: Build + run: dotnet build + + - name: Start dev database + run: ./install.sh configure-dev-db start + + - name: Ensure browsers are installed + run: pwsh apps/server/Tests/AliasVault.E2ETests/bin/Debug/net9.0/playwright.ps1 install --with-deps + + - name: Run AdminTests with retry + uses: nick-fields/retry@v3 + with: + timeout_minutes: 60 + max_attempts: 3 + command: dotnet test apps/server/Tests/AliasVault.E2ETests --no-build --verbosity normal --filter "Category=AdminTests" + + - name: Upload Test Results + if: always() + uses: actions/upload-artifact@v4 + with: + name: admin-test-results + path: TestResults-Admin.xml + + client-tests: + timeout-minutes: 60 + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + shard: [1, 2, 3, 4, 5] + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: 9.0.x + + - name: Install dependencies + run: dotnet workload install wasm-tools + + - name: Build + run: dotnet build + + - name: Start dev database + run: ./install.sh configure-dev-db start + + - name: Ensure browsers are installed + run: pwsh apps/server/Tests/AliasVault.E2ETests/bin/Debug/net9.0/playwright.ps1 install --with-deps + + - name: Run ClientTests with retry (Shard ${{ matrix.shard }}) + uses: nick-fields/retry@v3 + with: + timeout_minutes: 60 + max_attempts: 3 + command: dotnet test apps/server/Tests/AliasVault.E2ETests --no-build --verbosity normal --filter "FullyQualifiedName~.E2ETests.Tests.Client.Shard${{ matrix.shard }}."