Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
allow-unsafe-pr-checkout: true

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
Expand Down Expand Up @@ -72,6 +74,8 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
allow-unsafe-pr-checkout: true

- name: Setup Node.js
uses: actions/setup-node@v4
Expand Down Expand Up @@ -103,6 +107,8 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
allow-unsafe-pr-checkout: true

- name: Setup Node.js
uses: actions/setup-node@v4
Expand All @@ -129,6 +135,49 @@ jobs:
env:
NEXT_PUBLIC_STELLAR_RECEIVER_ADDRESS: ${{ secrets.NEXT_PUBLIC_STELLAR_RECEIVER_ADDRESS || 'GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWNA' }}

- name: Build Storybook
run: npm run storybook:build -w frontend
env:
NEXT_PUBLIC_STELLAR_RECEIVER_ADDRESS: ${{ secrets.NEXT_PUBLIC_STELLAR_RECEIVER_ADDRESS || 'GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWNA' }}

- name: Upload Storybook artifact
uses: actions/upload-artifact@v4
with:
name: storybook-static
path: frontend/storybook-static
if-no-files-found: warn

deploy-storybook:
name: Deploy Storybook to GitHub Pages
runs-on: ubuntu-latest
needs: build-frontend
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
permissions:
contents: read
pages: write
id-token: write
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- name: Download Storybook artifact
uses: actions/download-artifact@v4
with:
name: storybook-static
path: storybook-static

- name: Setup Pages
uses: actions/configure-pages@v4

- name: Upload artifact to Pages
uses: actions/upload-pages-artifact@v3
with:
path: storybook-static

- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4

security-scan:
name: Security Scan
runs-on: ubuntu-latest
Expand All @@ -139,6 +188,8 @@ jobs:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha || github.ref }}
repository: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
allow-unsafe-pr-checkout: true

- name: Setup Node.js
uses: actions/setup-node@v4
Expand Down
197 changes: 197 additions & 0 deletions .github/workflows/pr-validation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
name: PR Validation

# This workflow uses `pull_request` (not `pull_request_target`) so it runs from
# the PR branch's workflow file and safely handles fork checkouts without
# needing `allow-unsafe-pr-checkout: true`.
#
# For first-time contributors from forks, a maintainer with write access
# must approve the workflow run before it executes.

on:
pull_request:
types: [opened, synchronize, reopened]

jobs:
build-contracts:
name: Build Smart Contracts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable

- name: Cache Cargo dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
contracts/target/
key: ${{ runner.os }}-cargo-pr-${{ hashFiles('contracts/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-pr-
${{ runner.os }}-cargo-

- name: Check formatting
run: |
cd contracts
cargo fmt --all -- --check

- name: Run Clippy
run: |
cd contracts
cargo clippy -- -D warnings

- name: Build contracts
run: |
cd contracts
cargo build --release

- name: Verify contract build output
run: |
cd contracts
ls -lh target/release/*.so 2>/dev/null || echo "Library built successfully"

- name: Run contract tests
run: |
cd contracts
cargo test

- name: Upload contract artifacts
uses: actions/upload-artifact@v4
with:
name: contract-libs-pr
path: contracts/target/release/*.so
if-no-files-found: warn

build-backend:
name: Build Backend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Verify lock file
run: |
if [ ! -f package-lock.json ]; then
echo "Error: root package-lock.json is missing. Run 'npm install --package-lock-only' in the project root"
exit 1
fi

- name: Install dependencies
run: |
npm config set fetch-retries 5
npm config set fetch-retry-mintimeout 20000
npm config set fetch-retry-maxtimeout 120000
npm ci

- name: Build backend
run: npm run build -w backend

build-frontend:
name: Build Frontend
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Verify lock file
run: |
if [ ! -f package-lock.json ]; then
echo "Error: root package-lock.json is missing. Run 'npm install --package-lock-only' in the project root"
exit 1
fi

- name: Install dependencies
run: |
npm config set fetch-retries 5
npm config set fetch-retry-mintimeout 20000
npm config set fetch-retry-maxtimeout 120000
npm ci

- name: Build frontend
run: npm run build -w frontend
env:
NEXT_PUBLIC_STELLAR_RECEIVER_ADDRESS: ${{ vars.NEXT_PUBLIC_STELLAR_RECEIVER_ADDRESS || 'GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWNA' }}

- name: Build Storybook
run: npm run storybook:build -w frontend
env:
NEXT_PUBLIC_STELLAR_RECEIVER_ADDRESS: ${{ vars.NEXT_PUBLIC_STELLAR_RECEIVER_ADDRESS || 'GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWNA' }}

security-scan:
name: Security Scan
runs-on: ubuntu-latest
permissions:
security-events: write
contents: read
steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'

- name: Install root dependencies (for audit)
run: npm ci --ignore-scripts

- name: npm audit (report only — pre-existing vulns)
run: npm audit --audit-level=critical --workspaces --include-workspace-root || true

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable

- name: Cache Cargo dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
key: ${{ runner.os }}-cargo-audit-pr-${{ hashFiles('contracts/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-audit-pr-
${{ runner.os }}-cargo-audit-

- name: Install cargo-audit
run: |
if ! command -v cargo-audit &> /dev/null; then
cargo install --locked cargo-audit --version 0.21.2
fi

- name: cargo audit (Rust dependencies)
working-directory: contracts
run: cargo audit || true

- name: Run Trivy vulnerability scanner
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
scan-ref: '.'
format: 'sarif'
output: 'trivy-results.sarif'

- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v4
with:
sarif_file: 'trivy-results.sarif'
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ prof/

# Deployment / CI artifacts
.github/workflows/cache/
storybook-static/
.vercel/
.netlify/
.output/
Expand Down
60 changes: 60 additions & 0 deletions frontend/.storybook/main.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { StorybookConfig } from '@storybook/nextjs';

const config: StorybookConfig = {
stories: [
'../src/**/*.stories.@(js|jsx|mjs|ts|tsx)',
'../src/**/*.mdx',
],

addons: [
'@storybook/addon-links',
'@storybook/addon-essentials',
'@storybook/addon-a11y',
'@storybook/addon-interactions',
'@storybook/addon-themes',
],

framework: {
name: '@storybook/nextjs',
options: {},
},

docs: {
autodocs: 'tag',
},

staticDirs: ['../public'],

features: {
experimentalRSC: false,
},

typescript: {
check: false,
reactDocgen: 'react-docgen-typescript',
reactDocgenTypescriptOptions: {
compilerOptions: {
allowSyntheticDefaultImports: true,
esModuleInterop: true,
},
shouldExtractLiteralValuesFromEnum: true,
shouldRemoveUndefinedFromOptional: true,
propFilter: (prop) =>
prop.parent ? !/node_modules/.test(prop.parent.fileName) : true,
},
},

webpackFinal: async (config) => {
// Reset Next.js's aggressive splitChunks config which conflicts with
// Storybook's internal webpack compilation lifecycle.
// The next.config.js cacheGroups use Next.js's own webpack instance,
// but Storybook uses node_modules webpack — causing a compilation
// instance mismatch at DefinePlugin.getCompilationHooks.
if (config.optimization) {
config.optimization.splitChunks = false;
}
return config;
},
};

export default config;
Loading
Loading