Skip to content

Merge pull request #745 from iota-uz/codex/dashboard-builder-applet-u… #4182

Merge pull request #745 from iota-uz/codex/dashboard-builder-applet-u…

Merge pull request #745 from iota-uz/codex/dashboard-builder-applet-u… #4182

Workflow file for this run

name: Test, lint & build
on:
push:
branches:
- staging
- main
pull_request:
branches:
- staging
- main
types:
- opened
- reopened
- synchronize
- ready_for_review
jobs:
changes:
runs-on: blacksmith-8vcpu-ubuntu-2404
outputs:
backend: ${{ steps.filter.outputs.backend }}
e2e: ${{ steps.filter.outputs.e2e }}
docs: ${{ steps.filter.outputs.docs }}
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Detect changed paths
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
backend:
- 'cmd/**'
- 'components/**'
- 'internal/**'
- 'modules/**'
- 'pkg/**'
- 'migrations/**'
- 'scripts/**'
- 'styles/**'
- 'ui/**'
- 'tools.go'
- 'go.mod'
- 'go.sum'
- 'go.work'
- 'Justfile'
- 'Dockerfile*'
- 'compose*.yml'
- 'package.json'
- 'pnpm-lock.yaml'
- '.github/scripts/**'
- '.github/workflows/test.yml'
e2e:
- 'e2e/**'
docs:
- 'docs/**'
security-gosec:
needs: changes
if: |
(github.event_name != 'pull_request' || !github.event.pull_request.draft) &&
(github.event_name == 'workflow_dispatch' || github.event_name == 'push' || needs.changes.outputs.backend == 'true')
concurrency:
group: security-scan-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 10
name: Security Scan (gosec)
env:
GOPRIVATE: github.com/iota-uz/*
GONOSUMDB: github.com/iota-uz/*
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Go
uses: useblacksmith/setup-go@v6
with:
go-version: "1.24.13"
- name: Download Go dependencies
run: go mod download
- name: Install gosec
run: go install github.com/securego/gosec/v2/cmd/gosec@v2.22.2
- name: Install just
uses: extractions/setup-just@v3
with:
just-version: "1.46.0"
- name: Run gosec (production entrypoints)
run: just gosec
lint-and-format:
needs: changes
if: |
(github.event_name != 'pull_request' || !github.event.pull_request.draft) &&
(github.event_name == 'workflow_dispatch' || github.event_name == 'push' || needs.changes.outputs.backend == 'true')
concurrency:
group: lint-and-format-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 15
name: Code Quality & Formatting
env:
GOPRIVATE: github.com/iota-uz/*
GONOSUMDB: github.com/iota-uz/*
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Go
uses: useblacksmith/setup-go@v6
with:
go-version: "1.24.13"
- name: Install golangci-lint
run: |
if ! command -v golangci-lint >/dev/null; then
go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6
fi
golangci-lint version
- name: Install templ
run: |
go install github.com/a-h/templ/cmd/templ@v0.3.857
templ --help
- name: Install air
run: |
go install github.com/air-verse/air@latest
air -v
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9.15.0
- name: Install pg_formatter for SQL formatting
run: |
sudo apt-get update
sudo apt-get install -y pgformatter
- name: Install just
uses: extractions/setup-just@v3
with:
just-version: "1.46.0"
- name: Download Go dependencies
run: go mod download
- name: Install applet CLI
run: go install github.com/iota-uz/applets/cmd/applet@latest
- name: Test generated files are up to date
run: |
just generate
git diff --exit-code
- name: Check translation files
run: just check tr
- name: Enforce applet SDK dependency policy
run: just applet deps-check
- name: Check templ files formatting
run: |
templ fmt .
if [ -n "$(git diff --name-only)" ]; then
echo "Error: Some templ files are not properly formatted"
git diff
exit 1
fi
echo "All templ files are properly formatted"
- name: Check Go files formatting
run: |
go fmt ./...
if [ -n "$(git diff --name-only)" ]; then
echo "Error: Some Go files are not properly formatted"
git diff
exit 1
fi
echo "All Go files are properly formatted"
- name: Check SQL files formatting
run: |
mkdir -p /tmp/formatted
find modules -name "*.sql" -type f | while read -r sqlfile; do
pg_format "$sqlfile" > "/tmp/formatted/$(basename "$sqlfile")"
if ! diff -q "$sqlfile" "/tmp/formatted/$(basename "$sqlfile")" > /dev/null; then
echo "Error: $sqlfile is not properly formatted"
diff "$sqlfile" "/tmp/formatted/$(basename "$sqlfile")"
exit 1
fi
done
echo "All SQL files are properly formatted"
- name: Run go vet
run: go vet -tags dev ./...
- name: Check applet RPC contracts
run: just applet rpc-check bichat
- name: Install root dependencies (for Tailwind CSS)
run: pnpm install --frozen-lockfile --ignore-scripts
- name: Install BiChat applet dependencies
run: pnpm install --frozen-lockfile -C modules/bichat/presentation/web
- name: Run applet DX guardrails
run: |
applet doctor
- name: Typecheck BiChat applet
run: pnpm -C modules/bichat/presentation/web exec tsc --noEmit
- name: Generate CSS files
run: just css
- name: Run golangci-lint (unused variables/functions)
run: just check lint
test-unit-integration:
needs: changes
if: |
(github.event_name != 'pull_request' || !github.event.pull_request.draft) &&
(github.event_name == 'workflow_dispatch' || github.event_name == 'push' || needs.changes.outputs.backend == 'true')
concurrency:
group: unit-integration-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 25
name: Unit & Integration Tests
env:
GOPRIVATE: github.com/iota-uz/*
GONOSUMDB: github.com/iota-uz/*
services:
postgres:
image: postgres:17
ports:
- 5432:5432
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: iota_erp
POSTGRES_INITDB_ARGS: "-c max_connections=1000 -c shared_buffers=2GB \
-c effective_cache_size=6GB -c maintenance_work_mem=512MB \
-c work_mem=32MB -c fsync=off -c synchronous_commit=off \
-c full_page_writes=off -c wal_buffers=64MB \
-c max_wal_size=4GB -c min_wal_size=1GB \
-c checkpoint_completion_target=0.9 -c wal_compression=on \
-c random_page_cost=1.0 -c seq_page_cost=1.0 \
-c cpu_tuple_cost=0.01 -c cpu_index_tuple_cost=0.001 \
-c cpu_operator_cost=0.0005 -c log_min_duration_statement=0 \
-c log_statement=none -c log_checkpoints=off \
-c log_connections=off -c log_disconnections=off \
-c autovacuum=off -c track_activities=off \
-c track_counts=off -c jit=off -c huge_pages=try \
-c temp_buffers=128MB -c max_prepared_transactions=0 \
-c bgwriter_delay=10000ms -c bgwriter_lru_maxpages=0 \
-c effective_io_concurrency=200 -c maintenance_io_concurrency=200 \
-c max_parallel_workers_per_gather=4 -c max_parallel_workers=8 \
-c max_parallel_maintenance_workers=4 -c default_statistics_target=10 \
-c enable_partitionwise_join=on -c enable_partitionwise_aggregate=on"
options: >-
--tmpfs /var/lib/postgresql/data:rw,size=6g,noatime
--health-cmd="pg_isready -U postgres -d iota_erp"
--health-interval=5s --health-timeout=5s --health-retries=5
redis:
image: redis:latest
ports:
- 6379:6379
options: >-
--tmpfs /data:rw,size=6g
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Go
uses: useblacksmith/setup-go@v6
with:
go-version: "1.24.13"
- name: Install just
uses: extractions/setup-just@v3
with:
just-version: "1.46.0"
- name: Install tools
run: |
# Install templ (needed for CSS generation)
go install github.com/a-h/templ/cmd/templ@v0.3.857
- name: Download Go dependencies
run: go mod download
- name: Wait for PostgreSQL to be ready
run: |
until pg_isready -h localhost -p 5432 -U postgres -d iota_erp; do
echo "Waiting for postgres..."
sleep 1
done
- name: Validate PostgreSQL max connections
env:
DB_HOST: localhost
DB_PORT: 5432
DB_NAME: iota_erp
DB_USER: postgres
DB_PASSWORD: postgres
run: |
max_connections=$(psql "host=$DB_HOST port=$DB_PORT dbname=$DB_NAME user=$DB_USER password=$DB_PASSWORD sslmode=disable" -tAc "SHOW max_connections;" | tr -d '[:space:]')
echo "PostgreSQL max_connections=$max_connections"
min_expected=300
if [ -z "$max_connections" ] || [ "$max_connections" -lt "$min_expected" ]; then
echo "Error: max_connections is too low (expected >= $min_expected, got ${max_connections:-empty})"
exit 1
fi
- name: Test just recipes
env:
DB_HOST: localhost
DB_PORT: 5432
DB_NAME: iota_erp
DB_USER: postgres
DB_PASSWORD: postgres
run: |
just db migrate up
just db migrate up
just db seed
just db seed
- name: Run tests with coverage
env:
DB_HOST: localhost
DB_PORT: 5432
DB_NAME: iota_erp
DB_USER: postgres
DB_PASSWORD: postgres
run: go test -tags dev -v ./... -coverprofile=coverage.out -covermode=atomic
- name: Generate coverage report
env:
COVERAGE_THRESHOLD: 10
COVERAGE_MAX_FILES_DISPLAY: 100
COVERAGE_IGNORE_PATTERNS: "cmd/,*_templ.go,viewmodels/"
run: node ./.github/scripts/coverage-report.cjs --file coverage.out --output github
- name: Generate HTML coverage report
run: go tool cover -html=coverage.out -o coverage.html
- name: Upload coverage to artifacts
uses: actions/upload-artifact@v4
with:
name: code-coverage
path: |
coverage.out
coverage.html
- name: Test database migrate down
env:
DB_HOST: localhost
DB_PORT: 5432
DB_NAME: iota_erp
DB_USER: postgres
DB_PASSWORD: postgres
run: just db migrate down
test-e2e:
needs: changes
if: |
(github.event_name != 'pull_request' || !github.event.pull_request.draft) &&
(github.event_name == 'workflow_dispatch' || github.event_name == 'push' || needs.changes.outputs.backend == 'true' || needs.changes.outputs.e2e == 'true')
concurrency:
group: e2e-tests-${{ github.event.pull_request.number || github.ref }}-${{ matrix.shardIndex }}
cancel-in-progress: true
runs-on: blacksmith-8vcpu-ubuntu-2404
timeout-minutes: 20
name: E2E Tests (shard ${{ matrix.shard }})
strategy:
fail-fast: false
matrix:
include:
- shard: 1/4
shardIndex: 1
- shard: 2/4
shardIndex: 2
- shard: 3/4
shardIndex: 3
- shard: 4/4
shardIndex: 4
env:
GOPRIVATE: github.com/iota-uz/*
GONOSUMDB: github.com/iota-uz/*
services:
postgres:
image: postgres:17
ports:
- 5432:5432
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: iota_erp_e2e
POSTGRES_INITDB_ARGS: "-c max_connections=1000 -c shared_buffers=2GB \
-c effective_cache_size=6GB -c maintenance_work_mem=512MB \
-c work_mem=32MB -c fsync=off -c synchronous_commit=off \
-c full_page_writes=off -c wal_buffers=64MB \
-c max_wal_size=4GB -c min_wal_size=1GB \
-c checkpoint_completion_target=0.9 -c wal_compression=on \
-c random_page_cost=1.0 -c seq_page_cost=1.0 \
-c cpu_tuple_cost=0.01 -c cpu_index_tuple_cost=0.001 \
-c cpu_operator_cost=0.0005 -c log_min_duration_statement=0 \
-c log_statement=none -c log_checkpoints=off \
-c log_connections=off -c log_disconnections=off \
-c autovacuum=off -c track_activities=off \
-c track_counts=off -c jit=off -c huge_pages=try \
-c temp_buffers=128MB -c max_prepared_transactions=0 \
-c bgwriter_delay=10000ms -c bgwriter_lru_maxpages=0 \
-c effective_io_concurrency=200 -c maintenance_io_concurrency=200 \
-c max_parallel_workers_per_gather=4 -c max_parallel_workers=8 \
-c max_parallel_maintenance_workers=4 -c default_statistics_target=10 \
-c enable_partitionwise_join=on -c enable_partitionwise_aggregate=on"
options: >-
--tmpfs /var/lib/postgresql/data:rw,size=6g,noatime
--health-cmd="pg_isready -U postgres -d iota_erp_e2e"
--health-interval=5s --health-timeout=5s --health-retries=5
redis:
image: redis:latest
ports:
- 6379:6379
options: >-
--tmpfs /data:rw,size=6g
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Go
uses: useblacksmith/setup-go@v6
with:
go-version: "1.24.13"
- name: Install just
uses: extractions/setup-just@v3
with:
just-version: '1.46.0'
- name: Install tools
run: |
# Install templ (needed for CSS generation)
go install github.com/a-h/templ/cmd/templ@v0.3.857
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "20"
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9.15.0
- name: Get pnpm store directory
id: pnpm-store
run: echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_OUTPUT
- name: Cache pnpm store
uses: actions/cache@v4
with:
path: ${{ steps.pnpm-store.outputs.STORE_PATH }}
key: ${{ runner.os }}-pnpm-e2e-${{ hashFiles('pnpm-lock.yaml', 'e2e/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-e2e-
- name: Download Go dependencies
run: go mod download
- name: Install root dependencies (for Tailwind CSS)
run: pnpm install --frozen-lockfile --ignore-scripts
- name: Generate CSS files
run: just css
- name: Setup E2E database
env:
DB_NAME: iota_erp_e2e
run: |
just e2e reset
- name: Install Node dependencies
run: pnpm install
working-directory: e2e
- name: Cache Playwright browsers
id: playwright-cache
uses: actions/cache@v4
with:
path: ~/.cache/ms-playwright
key: ${{ runner.os }}-playwright-${{ hashFiles('e2e/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-playwright-
- name: Install Playwright system dependencies
run: npx playwright install-deps chromium
working-directory: e2e
- name: Install Playwright Browsers
if: steps.playwright-cache.outputs.cache-hit != 'true'
run: npx playwright install chromium
working-directory: e2e
- name: Start Go Server
env:
SESSION_DURATION: 720h
DOMAIN: localhost
PORT: 3201
DB_HOST: localhost
DB_PORT: 5432
DB_NAME: iota_erp_e2e
DB_USER: postgres
DB_PASSWORD: postgres
SID_COOKIE_KEY: sid
GO_APP_ENV: production
ENABLE_TEST_ENDPOINTS: true
TOTP_ENCRYPTION_KEY: e2e-test-encryption-key-32bytes!!
OIDC_ISSUER_URL: https://localhost:3201/oidc
OIDC_CRYPTO_KEY: MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=
run: |
go run -tags dev cmd/server/main.go > /tmp/server.log 2>&1 &
SERVER_PID=$!
echo "SERVER_PID=$SERVER_PID" >> $GITHUB_ENV
# Wait for server to be ready (timeout: 60 seconds)
timeout 60 bash -c 'until curl -s http://localhost:3201 > /dev/null; do sleep 1; done'
echo "Server started successfully"
- name: Run Playwright Tests
env:
BASE_URL: http://localhost:3201
run: npx playwright test --shard=${{ matrix.shard }}
working-directory: e2e
- name: Stop Go Server
if: always()
run: kill ${{ env.SERVER_PID }} || true
- name: Show Server Logs
if: failure()
run: |
echo "=== Server Logs ==="
cat /tmp/server.log || echo "No server logs found"
- name: Upload Playwright Report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report-${{ matrix.shardIndex }}
path: e2e/playwright-report/
retention-days: 30
- name: Upload Test Results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ matrix.shardIndex }}
path: e2e/test-results/
retention-days: 30
- name: Upload Server Logs
if: failure()
uses: actions/upload-artifact@v4
with:
name: server-logs-${{ matrix.shardIndex }}
path: /tmp/server.log
retention-days: 7
docs-build:
needs: changes
if: |
(github.event_name != 'pull_request' || !github.event.pull_request.draft) &&
(github.event_name == 'workflow_dispatch' || github.event_name == 'push' || needs.changes.outputs.docs == 'true')
concurrency:
group: docs-build-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
name: Documentation Build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 9.15.0
- name: Install documentation dependencies
working-directory: ./docs
run: pnpm install --frozen-lockfile
- name: Validate Mermaid diagrams
working-directory: ./docs
run: pnpm run check:mermaid
- name: Build documentation
working-directory: ./docs
run: pnpm build
- name: Check documentation build artifacts
run: |
if [ ! -d "docs/out" ]; then
echo "Error: Documentation build output not found"
exit 1
fi
if [ ! -f "docs/out/index.html" ]; then
echo "Error: Documentation index.html not found"
exit 1
fi
echo "Documentation build successful"