Skip to content

chore(deps): bump @otplib/plugin-crypto-node from 13.4.1 to 13.5.0 (#… #6795

chore(deps): bump @otplib/plugin-crypto-node from 13.4.1 to 13.5.0 (#…

chore(deps): bump @otplib/plugin-crypto-node from 13.4.1 to 13.5.0 (#… #6795

# SPDX-License-Identifier: Apache-2.0
name: Test Frontend
on:
push:
branches:
- main
- release/**
pull_request:
branches:
- main
- release/**
workflow_dispatch:
defaults:
run:
shell: bash
env:
LC_ALL: C.UTF-8
permissions:
contents: read
concurrency:
group: pr-checks-${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
build:
name: Build | Front-end
runs-on: transaction-tools-linux-medium
steps:
- name: Harden Runner
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit
- name: Checkout Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install pnpm
uses: step-security/action-setup@7734f5522f614a4cf2820ad19aa26717c7abfd96 # v6.0.9
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
node-version-file: package.json
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Build Linux executable
run: pnpm run build:linux:e2e
working-directory: front-end
- name: Cache Build Artifacts
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: front-end/release/linux-unpacked
key: build-artifacts-${{ github.sha }}
test:
name: Automation | ${{ matrix.test-suite.name }}
runs-on: transaction-tools-linux-medium
needs: build
strategy:
fail-fast: false
matrix:
test-suite:
- name: Local Basic
grep: '@local-basic'
soloRequired: false
backendRequired: false
sharedEnvironment: true
workers: 2
- name: Shared E2E
grep: '@local-transactions|@organization-basic|@organization-advanced'
soloRequired: true
backendRequired: true
sharedEnvironment: true
workers: 8
steps:
- name: Harden Runner
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit
- name: Checkout Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install pnpm
uses: step-security/action-setup@7734f5522f614a4cf2820ad19aa26717c7abfd96 # v6.0.9
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
node-version-file: package.json
- name: Install Docker Compose Plugin
if: ${{ matrix.test-suite.backendRequired }}
run: |
set -euo pipefail
if docker compose version >/dev/null 2>&1; then
docker compose version
exit 0
fi
sudo install -d /usr/local/lib/docker/cli-plugins
sudo curl -fLo /usr/local/lib/docker/cli-plugins/docker-compose \
https://github.com/docker/compose/releases/download/v2.29.1/docker-compose-linux-x86_64
sudo chmod +x /usr/local/lib/docker/cli-plugins/docker-compose
docker compose version
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Setup Kind
if: ${{ matrix.test-suite.soloRequired }}
uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0
with:
install_only: true
node_image: kindest/node:v1.31.4@sha256:2cb39f7295fe7eafee0842b1052a599a4fb0f8bcf3f83d96c7f4864c357c6c30
version: v0.26.0
kubectl_version: v1.31.4
verbosity: 3
wait: 120s
- name: Install Solo CLI
if: ${{ matrix.test-suite.soloRequired }}
run: |
set -euo pipefail
npm install -g @hashgraph/solo@0.64.2
solo --version
kind --version
- name: Deploy Solo
if: ${{ matrix.test-suite.soloRequired }}
id: solo_deploy
env:
SOLO_CLUSTER_NAME: solo
SOLO_NAMESPACE: solo
SOLO_CLUSTER_SETUP_NAMESPACE: solo-cluster
SOLO_DEPLOYMENT: solo-deployment
run: |
set -euo pipefail
kind create cluster -n "${SOLO_CLUSTER_NAME}"
solo one-shot single deploy | tee solo-deploy.log
- name: Check port forwarding
if: ${{ matrix.test-suite.soloRequired }}
run: |
set -euo pipefail
wait_for_tcp() {
local host="$1"
local port="$2"
timeout 1 bash -c ">/dev/tcp/${host}/${port}" >/dev/null 2>&1
}
ps -ef | grep port-forward || true
for attempt in $(seq 1 60); do
if curl -fsS http://localhost:8080/api/v1/network/nodes >/dev/null 2>&1 && \
curl -fsS http://localhost:8081/api/v1/network/nodes >/dev/null 2>&1 && \
wait_for_tcp 127.0.0.1 50211; then
echo "Solo port forwarding is ready on attempt ${attempt}"
break
fi
if [ "${attempt}" -eq 60 ]; then
echo "Solo port forwarding did not become ready in time" >&2
ps -ef | grep port-forward || true
kubectl get pods -A || true
exit 1
fi
echo "Waiting for Solo port forwarding... attempt ${attempt}/60"
sleep 5
done
curl -fsS http://localhost:8080/api/v1/network/nodes
curl -fsS http://localhost:8081/api/v1/network/nodes
wait_for_tcp 127.0.0.1 50211
- name: Deploy Back-end
if: ${{ matrix.test-suite.backendRequired }}
working-directory: back-end
env:
BACKEND_COMPOSE_FILE: docker-compose.yaml
run: |
set -euo pipefail
wait_for_tcp() {
local host="$1"
local port="$2"
timeout 1 bash -c ">/dev/tcp/${host}/${port}" >/dev/null 2>&1
}
# Install Linux packages required for mkcert and host-to-container port proxies.
sudo apt-get update -q
sudo apt-get install -y --no-install-recommends libnss3-tools socat
if ! command -v mkcert >/dev/null 2>&1; then
curl -fsSL https://github.com/FiloSottile/mkcert/releases/download/v1.4.4/mkcert-v1.4.4-linux-amd64 \
-o /tmp/mkcert
sudo install -m755 /tmp/mkcert /usr/local/bin/mkcert
fi
mkcert -install
mkdir -p cert
mkcert -key-file cert/key.pem -cert-file cert/cert.pem localhost
for dir in . apps/api apps/chain apps/notifications typeorm scripts; do
cp "${dir}/example.env" "${dir}/.env"
done
FE_VERSION="$(node -p "require('../front-end/package.json').version")"
sed -i "s/^LATEST_SUPPORTED_FRONTEND_VERSION=.*/LATEST_SUPPORTED_FRONTEND_VERSION=${FE_VERSION}/" apps/api/.env
sed -i "s/^MINIMUM_SUPPORTED_FRONTEND_VERSION=.*/MINIMUM_SUPPORTED_FRONTEND_VERSION=${FE_VERSION}/" apps/api/.env
sed -i "s/^MINIMUM_SUPPORTED_FRONTEND_VERSION=.*/MINIMUM_SUPPORTED_FRONTEND_VERSION=${FE_VERSION}/" apps/notifications/.env
sed -i 's/^EMAIL_API_SECURE=.*/EMAIL_API_SECURE=false/' apps/notifications/.env
sed -i 's/^ANONYMOUS_MINUTE_LIMIT=.*/ANONYMOUS_MINUTE_LIMIT=99999/' apps/api/.env
sed -i 's/^ANONYMOUS_FIVE_SECOND_LIMIT=.*/ANONYMOUS_FIVE_SECOND_LIMIT=99999/' apps/api/.env
sed -i 's/^GLOBAL_MINUTE_LIMIT=.*/GLOBAL_MINUTE_LIMIT=99999/' apps/api/.env
sed -i 's/^GLOBAL_SECOND_LIMIT=.*/GLOBAL_SECOND_LIMIT=99999/' apps/api/.env
export COMPOSE_PARALLEL_LIMIT=4
if ! docker info &>/dev/null; then
echo "Docker daemon is not reachable; attempting to start it..."
sudo service docker start || true
sudo systemctl start docker || true
fi
for i in $(seq 1 15); do
if docker info &>/dev/null; then
break
fi
if [ "${i}" -eq 15 ]; then
echo "Docker daemon is unavailable after startup attempts" >&2
ls -l /var/run/docker.sock || true
sudo ls -l /var/run/docker.sock || true
ps -ef | grep -E 'dockerd|containerd' | grep -v grep || true
exit 1
fi
sleep 2
done
for attempt in $(seq 1 60); do
if curl -fsS http://localhost:8081/api/v1/network/nodes >/dev/null 2>&1 && \
wait_for_tcp 127.0.0.1 50211; then
echo "Solo network endpoints are ready on attempt ${attempt}"
break
fi
if [ "${attempt}" -eq 60 ]; then
echo "Solo network endpoints did not become ready in time" >&2
exit 1
fi
echo "Waiting for Solo network endpoints... attempt ${attempt}/60"
sleep 5
done
DOCKER_HOST_GATEWAY="$(docker network inspect bridge --format '{{(index .IPAM.Config 0).Gateway}}')"
if [ -z "${DOCKER_HOST_GATEWAY}" ]; then
echo "Failed to resolve the Docker bridge gateway IP" >&2
exit 1
fi
echo "Docker bridge gateway: ${DOCKER_HOST_GATEWAY}"
# Solo binds its localnet services to localhost on the runner.
# Docker containers reach the host via the bridge gateway, so expose the ports there first.
for port in 50211 8081; do
sudo bash -c "nohup socat TCP-LISTEN:${port},bind=${DOCKER_HOST_GATEWAY},fork,reuseaddr TCP:127.0.0.1:${port} >/tmp/socat-${port}.log 2>&1 &"
done
sleep 2
if ! curl -fsS "http://${DOCKER_HOST_GATEWAY}:8081/api/v1/network/nodes" >/dev/null 2>&1 || \
! wait_for_tcp "${DOCKER_HOST_GATEWAY}" 50211; then
echo "One or more Solo port proxies are not reachable on ${DOCKER_HOST_GATEWAY}" >&2
ss -lnt || true
sudo tail -n 50 /tmp/socat-50211.log || true
sudo tail -n 50 /tmp/socat-8081.log || true
exit 1
fi
for service in redis migration api chain notifications; do
for attempt in $(seq 1 3); do
if docker compose -f "${BACKEND_COMPOSE_FILE}" build --progress plain "${service}"; then
break
fi
if [ "${attempt}" -eq 3 ]; then
echo "docker compose build failed for ${service} after 3 attempts" >&2
exit 1
fi
echo "Build for ${service} failed on attempt ${attempt}; retrying in 15s..."
sleep 15
done
done
for attempt in $(seq 1 5); do
if docker compose -f "${BACKEND_COMPOSE_FILE}" up -d --no-build api chain notifications; then
break
fi
if [ "${attempt}" -eq 5 ]; then
echo "docker compose up failed after 5 attempts" >&2
docker compose -f "${BACKEND_COMPOSE_FILE}" ps || true
docker compose -f "${BACKEND_COMPOSE_FILE}" logs --tail=200 || true
exit 1
fi
echo "Attempt ${attempt} failed; retrying in 15s..."
sleep 15
done
# Wait for the API to become ready.
# The API serves HTTPS when NODE_ENV=development and cert files are present.
MAX_ATTEMPTS=60
for attempt in $(seq 1 "${MAX_ATTEMPTS}"); do
code="$(curl -k -s -o /dev/null -w '%{http_code}' https://localhost:3001/ 2>/dev/null || echo '000')"
if [[ "${code}" == "200" || "${code}" == "426" ]]; then
echo "Back-end is ready (attempt ${attempt})"
break
fi
if (( attempt % 10 == 0 )); then
echo "Waiting for back-end... attempt ${attempt}/${MAX_ATTEMPTS} (HTTP ${code})"
docker compose -f "${BACKEND_COMPOSE_FILE}" ps
docker compose -f "${BACKEND_COMPOSE_FILE}" logs --tail=50 api || true
fi
if [ "${attempt}" -eq "${MAX_ATTEMPTS}" ]; then
echo "Back-end did not become ready in time" >&2
docker compose -f "${BACKEND_COMPOSE_FILE}" ps || true
docker compose -f "${BACKEND_COMPOSE_FILE}" logs --tail=300 || true
exit 1
fi
sleep 5
done
docker compose -f "${BACKEND_COMPOSE_FILE}" ps
- name: Test API connectivity
if: ${{ matrix.test-suite.backendRequired }}
run: |
set -euo pipefail
code="$(curl -k -sS -o /dev/null -w '%{http_code}' https://localhost:3001/ || echo '000')"
echo "API connectivity check returned HTTP ${code}"
if [[ "${code}" != "200" && "${code}" != "426" ]]; then
echo "Unexpected HTTP status from API: ${code}" >&2
exit 1
fi
- name: Restore Build Artifacts
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
fail-on-cache-miss: true
path: front-end/release/linux-unpacked
key: build-artifacts-${{ github.sha }}
- name: Install xvfb
run: sudo apt-get update && sudo apt-get install -y xvfb
- name: Install Runtime Shared Libraries
run: sudo apt-get install --no-install-recommends -y xorg openbox libnss3 libasound2 libatk-adaptor libgtk-3-0
- name: Run Automation
env:
EXECUTABLE_PATH: ../front-end/release/linux-unpacked/hedera-transaction-tool
ENVIRONMENT: LOCALNET
PLAYWRIGHT_SHARED_ENV: ${{ matrix.test-suite.sharedEnvironment && 'true' || 'false' }}
PLAYWRIGHT_WORKERS: ${{ matrix.test-suite.workers }}
ORGANIZATION_URL: ${{ matrix.test-suite.backendRequired && 'https://localhost:3001' || '' }}
POSTGRES_HOST: ${{ matrix.test-suite.backendRequired && 'localhost' || '' }}
POSTGRES_PORT: ${{ matrix.test-suite.backendRequired && '5432' || '' }}
POSTGRES_DATABASE: ${{ matrix.test-suite.backendRequired && 'postgres' || '' }}
POSTGRES_USERNAME: ${{ matrix.test-suite.backendRequired && 'postgres' || '' }}
POSTGRES_PASSWORD: ${{ matrix.test-suite.backendRequired && 'postgres' || '' }}
run: xvfb-run -a npx playwright test --grep "${{ matrix.test-suite.grep }}"
working-directory: automation
- name: Upload Playwright Report
if: ${{ always() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: playwright-report-${{ matrix.test-suite.name }}
path: |
automation/reports/playwright
automation/reports/playwright-json
if-no-files-found: warn
retention-days: 3
- name: Upload Playwright Test Results
if: ${{ always() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: playwright-test-results-${{ matrix.test-suite.name }}
path: automation/test-results
if-no-files-found: warn
retention-days: 3
- name: Show Back-end Logs
if: ${{ failure() && matrix.test-suite.backendRequired }}
env:
BACKEND_COMPOSE_FILE: docker-compose.yaml
run: docker compose -f "${BACKEND_COMPOSE_FILE}" logs --tail=300
working-directory: back-end
- name: Collect Solo Diagnostics
if: ${{ always() && matrix.test-suite.soloRequired }}
run: |
set -euo pipefail
mkdir -p solo-diagnostics
kubectl get pods -A -o wide > solo-diagnostics/pods.txt || true
kubectl get events -A --sort-by=.lastTimestamp > solo-diagnostics/events.txt || true
kubectl describe pods -A > solo-diagnostics/pod-describe.txt || true
kubectl logs -A --all-containers --tail=500 > solo-diagnostics/all-pod-logs.txt || true
cp solo-deploy.log solo-diagnostics/solo-deploy.log || true
cp /tmp/socat-*.log solo-diagnostics/ 2>/dev/null || true
- name: Upload Solo Diagnostics
if: ${{ always() && matrix.test-suite.soloRequired }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: solo-diagnostics-${{ matrix.test-suite.name }}
path: solo-diagnostics
if-no-files-found: warn
retention-days: 3
- name: Export Kind Logs
if: ${{ always() && matrix.test-suite.soloRequired }}
run: kind export logs kind-logs --name solo || true
- name: Upload Kind Logs
if: ${{ failure() && matrix.test-suite.soloRequired }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: kind-logs-${{ matrix.test-suite.name }}
path: kind-logs
if-no-files-found: warn
retention-days: 3
- name: Stop Back-end Services
if: ${{ always() && matrix.test-suite.backendRequired }}
env:
BACKEND_COMPOSE_FILE: docker-compose.yaml
run: docker compose -f "${BACKEND_COMPOSE_FILE}" down --volumes --remove-orphans || true
working-directory: back-end
cleanup:
name: Cleanup | Build Cache
runs-on: transaction-tools-linux-medium
needs: [test]
permissions:
actions: write
steps:
- name: Harden Runner
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit
- name: Delete Build Artifacts Cache
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
if ! gh api --method DELETE \
"/repos/${{ github.repository }}/actions/caches?key=build-artifacts-${{ github.sha }}&ref=${{ github.ref }}"; then
echo "::warning::Failed to delete Actions cache build-artifacts-${{ github.sha }}"
fi
unit-test:
name: Unit Tests | ${{ matrix.test-suite.name }}
runs-on: transaction-tools-linux-medium
strategy:
fail-fast: false
matrix:
test-suite:
- name: Main Process
command: main:coverage
- name: Renderer Process
command: renderer
- name: Shared
command: shared
steps:
- name: Harden Runner
uses: step-security/harden-runner@05e31511f85b41b11d1cf0ef85d0992719546e2c # v2.21.0
with:
egress-policy: audit
- name: Checkout Code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install pnpm
uses: step-security/action-setup@7734f5522f614a4cf2820ad19aa26717c7abfd96 # v6.0.9
- name: Set up Node.js
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
cache: pnpm
cache-dependency-path: pnpm-lock.yaml
node-version-file: package.json
- name: Install Dependencies
run: pnpm install --frozen-lockfile
- name: Run Unit Tests
run: pnpm run test:${{ matrix.test-suite.command }}
working-directory: front-end
- name: Upload coverage to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
with:
token: ${{ secrets.CODECOV_TOKEN }}