Skip to content

feat(navigation): refactor to type-safe React Navigation with deep li… #644

feat(navigation): refactor to type-safe React Navigation with deep li…

feat(navigation): refactor to type-safe React Navigation with deep li… #644

Workflow file for this run

name: CI/CD Pipeline
on:
push:
branches: [main, dev, develop, 'feature/*']
pull_request:
branches: [main, dev, develop, 'feature/*']
env:
NODE_VERSION: '20'
RUST_VERSION: '1.88'
jobs:
commitlint:
name: Conventional Commit Check
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Cache node modules
uses: actions/cache@v4
id: cache-node-modules
with:
path: node_modules
key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci --legacy-peer-deps
- name: Validate PR commits
run: npx commitlint --from=origin/${{ github.event.pull_request.base.ref }} --to=HEAD --verbose
# ─────────────────────────────────────────────────────────
# TypeScript / React Native Checks
# ─────────────────────────────────────────────────────────
typescript-lint:
name: TypeScript Lint & Format
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Cache node modules
uses: actions/cache@v4
id: cache-node-modules
with:
path: node_modules
key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci --legacy-peer-deps
- name: Check formatting (Prettier)
id: prettier-check
run: npm run format:check
continue-on-error: true
- name: Auto-fix formatting issues
if: steps.prettier-check.outcome == 'failure'
run: |
echo "Formatting inconsistencies detected. Executing auto-fixer..."
npm run format:write || npx prettier --write "src/**/*.{ts,tsx,js,json,md}"
- name: Commit and Push formatting fixes
if: steps.prettier-check.outcome == 'failure'
uses: stefanzweifel/git-auto-commit-action@v5
with:
commit_message: 'chore: automated code formatting fixes via CI pipeline'
commit_user_name: 'github-actions[bot]'
commit_user_email: 'github-actions[bot]@users.noreply.github.com'
- name: Enforce strict layout check
if: steps.prettier-check.outcome == 'failure'
run: |
echo "❌ Code styles were inconsistent. Automated modifications have been pushed to your branch."
echo "Please pull latest changes locally or wait for the subsequent workflow run execution pass."
exit 1
- name: Run ESLint
run: npm run lint
npm-audit:
name: NPM Audit (High/Critical)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci --legacy-peer-deps
- name: Run NPM Audit
run: npx audit-ci --config audit-ci.json
typescript-typecheck:
name: TypeScript Type Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Cache node modules
uses: actions/cache@v4
id: cache-node-modules
with:
path: node_modules
key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci --legacy-peer-deps
- name: EVM ABI TypeChain (must match committed output)
run: npm run contracts:codegen:check
- name: Run TypeScript type check
run: npx tsc --noEmit
- name: Validate performance budgets
run: npm run performance:ci
typescript-tests:
name: TypeScript Tests (Sharded)
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3]
env:
SHARD: ${{ matrix.shard }}
SHARD_COUNT: 3
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Cache node modules
uses: actions/cache@v4
id: cache-node-modules
with:
path: node_modules
key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci --legacy-peer-deps
- name: Run sharded tests with coverage
run: |
echo "Running shard $SHARD of $SHARD_COUNT"
npm run test:shard
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: coverage-report-${{ matrix.shard }}
path: coverage/lcov.info
typescript-build:
name: TypeScript Build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Cache node modules
uses: actions/cache@v4
id: cache-node-modules
with:
path: node_modules
key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: Cache Expo build cache
uses: actions/cache@v4
with:
path: |
.expo
node_modules/.cache/expo
key: ${{ runner.os }}-expo-${{ hashFiles('package.json', 'app.json') }}
restore-keys: |
${{ runner.os }}-expo-
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci --legacy-peer-deps
- name: Patch metro exports for @expo/cli compatibility
run: |
node -e "
const fs=require('fs');
const p='node_modules/metro/package.json';
if(!fs.existsSync(p)) process.exit(0);
const m=JSON.parse(fs.readFileSync(p,'utf8'));
if(!m.exports||m.exports['./src/lib/TerminalReporter']) process.exit(0);
m.exports['./src/lib/TerminalReporter']='./src/lib/TerminalReporter.js';
fs.writeFileSync(p,JSON.stringify(m,null,2));
console.log('Patched metro exports to add ./src/lib/TerminalReporter');
"
- name: Run Expo export
run: npm run build
env:
EXPO_NO_TELEMETRY: 1
sonarcloud:
name: SonarCloud Analysis
runs-on: ubuntu-latest
needs: [typescript-tests]
if: github.event_name == 'pull_request' || github.ref == 'refs/heads/main'
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Download coverage report
uses: actions/download-artifact@v4
with:
name: coverage-report
path: coverage
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# ─────────────────────────────────────────────────────────
# Rust / Soroban Smart Contract Checks
# ─────────────────────────────────────────────────────────
rust-format:
name: Rust Format Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
components: rustfmt
- name: Check Rust formatting
run: cd contracts && cargo fmt --check
rust-clippy:
name: Rust Clippy Lint
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
components: clippy
- name: Cache Rust dependencies and target
uses: actions/cache@v4
with:
path: |
~/cargo/registry/index/
~/cargo/registry/cache/
~/cargo/git/db/
contracts/target/
key: ${{ runner.os }}-cargo-${{ env.RUST_VERSION }}-${{ hashFiles('contracts/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-${{ env.RUST_VERSION }}-
- name: Run Clippy
working-directory: ./contracts
run: cargo clippy --all-targets -- -D warnings
rust-tests:
name: Rust Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Cache Rust dependencies and target
uses: actions/cache@v4
with:
path: |
~/cargo/registry/index/
~/cargo/registry/cache/
~/cargo/git/db/
contracts/target/
key: ${{ runner.os }}-cargo-${{ env.RUST_VERSION }}-${{ hashFiles('contracts/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-${{ env.RUST_VERSION }}-
- name: Run Rust tests
working-directory: ./contracts
run: cargo test --verbose
rust-build:
name: Rust Build
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.RUST_VERSION }}
- name: Cache Rust dependencies and target
uses: actions/cache@v4
with:
path: |
~/cargo/registry/index/
~/cargo/registry/cache/
~/cargo/git/db/
contracts/target/
key: ${{ runner.os }}-cargo-${{ env.RUST_VERSION }}-${{ hashFiles('contracts/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-${{ env.RUST_VERSION }}-
- name: Build Rust contracts
working-directory: ./contracts
run: cargo build --release
# ─────────────────────────────────────────────────────────
# Load Testing
# ─────────────────────────────────────────────────────────
load-test:
name: k6 Load Test
runs-on: ubuntu-latest
continue-on-error: true
strategy:
fail-fast: false
matrix:
scenario: [subscription, billing, contract]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Prepare reports directory
run: mkdir -p load-tests/reports
- name: Install k6
run: |
sudo gpg -k
sudo gpg --no-default-keyring --keyring /usr/share/keyrings/k6-archive-keyring.gpg \
--keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb [signed-by=/usr/share/keyrings/k6-archive-keyring.gpg] https://dl.k6.io/deb stable main" \
| sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install -y k6
- name: Run k6 Load Test (${{ matrix.scenario }})
run: k6 run load-tests/run.js --env SCENARIO=${{ matrix.scenario }} --quiet
- name: Rename report for this scenario
if: always()
run: |
for ext in json md html; do
if [ -f "load-tests/reports/summary.$ext" ]; then
mv "load-tests/reports/summary.$ext" "load-tests/reports/${{ matrix.scenario }}.$ext"
fi
done
- name: Upload load test report
if: always()
uses: actions/upload-artifact@v4
with:
name: load-test-report-${{ matrix.scenario }}
path: load-tests/reports/
if-no-files-found: ignore
# ─────────────────────────────────────────────────────────
# Bundle Size Monitoring
# ─────────────────────────────────────────────────────────
bundle-size:
name: Bundle Size Check
runs-on: ubuntu-latest
env:
EXPO_NO_TELEMETRY: 1
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci --legacy-peer-deps
- name: Patch metro exports for @expo/cli compatibility
run: |
node -e "
const fs=require('fs');
const p='node_modules/metro/package.json';
if(!fs.existsSync(p)) process.exit(0);
const m=JSON.parse(fs.readFileSync(p,'utf8'));
if(!m.exports||m.exports['./src/lib/TerminalReporter']) process.exit(0);
m.exports['./src/lib/TerminalReporter']='./src/lib/TerminalReporter.js';
fs.writeFileSync(p,JSON.stringify(m,null,2));
console.log('Patched metro exports to add ./src/lib/TerminalReporter');
"
- name: Check bundle size (PR)
if: github.event_name == 'pull_request'
run: npx size-limit
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Build app
if: github.event_name != 'pull_request'
run: npm run build
- name: Check bundle size (Push)
if: github.event_name != 'pull_request'
run: npx size-limit --json > bundle-size-report.json
- name: Upload bundle size report
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
uses: actions/upload-artifact@v4
with:
name: bundle-size-report-${{ github.sha }}
path: bundle-size-report.json
# ─────────────────────────────────────────────────────────
# Performance Monitoring
# ─────────────────────────────────────────────────────────
performance:
name: Performance Budget Check
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Cache node modules
uses: actions/cache@v4
id: cache-node-modules
with:
path: node_modules
key: ${{ runner.os }}-node-${{ env.NODE_VERSION }}-${{ hashFiles('package-lock.json') }}
- name: Install dependencies
if: steps.cache-node-modules.outputs.cache-hit != 'true'
run: npm ci --legacy-peer-deps
- name: Run performance benchmark
run: npm run performance:benchmark
# ─────────────────────────────────────────────────────────
# Merge Protection (only on PRs)
# ─────────────────────────────────────────────────────────
merge-protection:
name: Merge Protection Check
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
needs:
[
commitlint,
typescript-lint,
typescript-typecheck,
typescript-tests,
typescript-build,
sonarcloud,
rust-format,
rust-clippy,
rust-tests,
rust-build,
load-test,
bundle-size,
performance,
]
steps:
- name: All checks passed
run: echo "All quality gates passed!"
# ─────────────────────────────────────────────────────────
# Full CI Summary (runs after all jobs)
# ─────────────────────────────────────────────────────────
ci-success:
name: CI Complete
runs-on: ubuntu-latest
if: always()
needs:
[
commitlint,
typescript-lint,
typescript-typecheck,
typescript-tests,
typescript-build,
sonarcloud,
rust-format,
rust-clippy,
rust-tests,
rust-build,
load-test,
bundle-size,
performance,
]
steps:
- name: Check for failures
run: |
if [ "${{ github.event_name }}" = "pull_request" ] && [ "${{ needs.commitlint.result }}" != "success" ]; then
echo "Conventional commit check failed"
exit 1
fi
if [ "${{ needs.typescript-lint.result }}" != "success" ] || \
[ "${{ needs.typescript-typecheck.result }}" != "success" ] || \
[ "${{ needs.typescript-tests.result }}" != "success" ] || \
[ "${{ needs.typescript-build.result }}" != "success" ] || \
[ "${{ needs.sonarcloud.result }}" != "success" ] || \
[ "${{ needs.rust-format.result }}" != "success" ] || \
[ "${{ needs.rust-clippy.result }}" != "success" ] || \
[ "${{ needs.rust-tests.result }}" != "success" ] || \
[ "${{ needs.rust-build.result }}" != "success" ] || \
[ "${{ needs.load-test.result }}" != "success" ] || \
[ "${{ needs.bundle-size.result }}" != "success" ] || \
[ "${{ needs.performance.result }}" != "success" ]; then
echo "One or more CI checks failed"
exit 1
fi
echo "All CI checks passed successfully!"