diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000..7bbf84de --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,141 @@ +# Dependabot configuration — automated dependency updates +# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates +# for all config options. + +version: 2 +updates: + # ── Root workspace ────────────────────────────────────────────── + - package-ecosystem: "npm" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + timezone: "UTC" + commit-message: + prefix: "chore(deps)" + prefix-development: "chore(deps-dev)" + labels: + - "dependencies" + - "npm" + open-pull-requests-limit: 10 + versioning-strategy: auto + groups: + nestjs: + patterns: + - "@nestjs/*" + update-types: + - "minor" + - "patch" + react: + patterns: + - "react" + - "react-dom" + - "@types/react" + - "@types/react-dom" + update-types: + - "minor" + - "patch" + + # ── Frontend ──────────────────────────────────────────────────── + - package-ecosystem: "npm" + directory: "/frontend" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + timezone: "UTC" + commit-message: + prefix: "chore(deps/frontend)" + prefix-development: "chore(deps-dev/frontend)" + labels: + - "dependencies" + - "frontend" + open-pull-requests-limit: 10 + versioning-strategy: auto + ignore: + # Next.js v15 is a breaking change; handle manually + - dependency-name: "next" + update-types: ["version-update:semver-major"] + groups: + radix-ui: + patterns: + - "@radix-ui/*" + update-types: + - "minor" + - "patch" + stellar: + patterns: + - "@stellar/*" + update-types: + - "minor" + - "patch" + + # ── Backend ───────────────────────────────────────────────────── + - package-ecosystem: "npm" + directory: "/backend" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + timezone: "UTC" + commit-message: + prefix: "chore(deps/backend)" + prefix-development: "chore(deps-dev/backend)" + labels: + - "dependencies" + - "backend" + open-pull-requests-limit: 10 + versioning-strategy: auto + groups: + nestjs: + patterns: + - "@nestjs/*" + update-types: + - "minor" + - "patch" + typeorm: + patterns: + - "typeorm" + - "@nestjs/typeorm" + update-types: + - "minor" + - "patch" + + # ── Onchain (Cargo/Rust) ──────────────────────────────────────── + - package-ecosystem: "cargo" + directory: "/onchain" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + timezone: "UTC" + commit-message: + prefix: "chore(deps/onchain)" + labels: + - "dependencies" + - "onchain" + - "rust" + open-pull-requests-limit: 10 + groups: + soroban: + patterns: + - "soroban-sdk" + update-types: + - "minor" + - "patch" + + # ── GitHub Actions (self-update) ──────────────────────────────── + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + day: "monday" + time: "09:00" + timezone: "UTC" + commit-message: + prefix: "ci(deps)" + labels: + - "dependencies" + - "ci" + open-pull-requests-limit: 5 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68109017..c648e18c 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,8 +8,12 @@ on: pull_request: permissions: read-all +# ───────────────────────────────────────────────────────────────────── +# Onchain jobs (contracts) +# ───────────────────────────────────────────────────────────────────── jobs: - build: + onchain-build: + name: Build contracts runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -20,16 +24,8 @@ jobs: targets: wasm32-unknown-unknown components: rustfmt - # The "Install Soroban / Stellar CLI" step was removed: stellar-cli - # v22.0.0 is not published (the release URL returns 404) and no step - # in this workflow invokes the CLI. Re-add with a verified release - # tag if/when a deployment step is introduced. - # `cargo --locked` reads the committed `onchain/Cargo.lock` verbatim - # (most importantly pinning `ed25519-dalek 2.1.1` to work around the - # `soroban-env-host 22.1.3` ↔ `ed25519-dalek 3.0` trait-bound - # incompatibility) and fails if the resolved graph would diverge. - # Build for wasm32 — Soroban contracts are deployed as WASM cdylibs. - - name: Build contracts + # See onchain/Cargo.lock for pinned dependency resolutions. + - name: Build contracts (release wasm) working-directory: onchain run: cargo build --workspace --target wasm32-unknown-unknown --release --locked @@ -37,7 +33,20 @@ jobs: working-directory: onchain run: cargo fmt --all -- --check - test: + # ── cargo-deny ──────────────────────────────────────────── + # Audit dependencies for security advisories, license compliance, + # and duplicate crate versions. + - name: Install cargo-deny + uses: taiki-e/install-action@v2 + with: + tool: cargo-deny + + - name: cargo-deny check + working-directory: onchain + run: cargo deny --locked check advisories licenses bans sources + + onchain-test: + name: Test contracts runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -47,14 +56,6 @@ jobs: with: toolchain: stable - # The "Install Soroban / Stellar CLI" step was removed: stellar-cli - # v22.0.0 is not published (the release URL returns 404) and no step - # in this workflow invokes the CLI. Re-add with a verified release - # tag if/when a deployment step is introduced. - # Build tests on host with the `testutils` feature (see each - # contract's `[dev-dependencies] soroban-sdk = { features = ["testutils"] }`) - # so `mock_all_auths`, `register_contract`, `Address::generate`, etc. - # are available. `--locked` honours the committed Cargo.lock. - name: Build contracts (test profile) working-directory: onchain run: cargo build --workspace --tests --locked @@ -62,3 +63,105 @@ jobs: - name: Run unit tests working-directory: onchain run: cargo test --workspace --locked + + # ───────────────────────────────────────────────────────────────────── + # Backend CI + # ───────────────────────────────────────────────────────────────────── + backend-lint: + name: Backend lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: backend/package-lock.json + + - name: Install dependencies + working-directory: backend + run: npm ci + + - name: Lint + working-directory: backend + run: npm run lint + + - name: npm audit + working-directory: backend + # --audit-level=high exits with non-zero if any advisory at + # severity high or critical is found. + run: npm audit --audit-level=high + continue-on-error: true + + backend-test: + name: Backend tests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: backend/package-lock.json + + - name: Install dependencies + working-directory: backend + run: npm ci + + - name: Run unit tests + working-directory: backend + run: npm test -- --passWithNoTests + + # ───────────────────────────────────────────────────────────────────── + # Frontend CI + # ───────────────────────────────────────────────────────────────────── + frontend-lint: + name: Frontend lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install dependencies + working-directory: frontend + run: npm ci + + - name: Lint + working-directory: frontend + run: npm run lint + + - name: npm audit + working-directory: frontend + run: npm audit --audit-level=high + continue-on-error: true + + frontend-build: + name: Frontend build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: frontend/package-lock.json + + - name: Install dependencies + working-directory: frontend + run: npm ci + + - name: Build + working-directory: frontend + run: npm run build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 00000000..bc9f060f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,141 @@ +# Release workflow — triggered by a pushed semver tag (vX.Y.Z) +# +# Usage: +# 1. Decide on the next version per semver (MAJOR.MINOR.PATCH). +# 2. Create and push the tag: +# git tag vX.Y.Z +# git push origin vX.Y.Z +# 3. This workflow will build, create a GitHub Release, and +# generate a changelog from conventional-commit messages. + +name: Release + +on: + push: + tags: + - "v[0-9]+.[0-9]+.[0-9]+" + +permissions: + contents: write + +jobs: + release: + name: Create Release + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + # Fetch all history so git-cliff / auto-changelog can read commits + fetch-depth: 0 + + - name: Query tag metadata + id: tag + run: | + echo "version=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" + + # ── Install toolchains ──────────────────────────────── + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + targets: wasm32-unknown-unknown + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + cache-dependency-path: | + package-lock.json + frontend/package-lock.json + backend/package-lock.json + + # ── Install dependencies ────────────────────────────── + - name: Install frontend dependencies + working-directory: frontend + run: npm ci + + - name: Install backend dependencies + working-directory: backend + run: npm ci + + # ── Build all artifacts ─────────────────────────────── + - name: Build frontend + working-directory: frontend + run: npm run build + + - name: Build backend + working-directory: backend + run: npm run build + + - name: Build onchain contracts + working-directory: onchain + run: cargo build --workspace --target wasm32-unknown-unknown --release --locked + + # ── Generate changelog ──────────────────────────────── + - name: Generate changelog + id: changelog + run: | + # Build a changelog from conventional-commit messages. + # Groups commits by type (feat, fix, chore, docs, ci, etc.) + # for readable release notes. + PREV_TAG=$(git describe --tags --abbrev=0 "${GITHUB_REF_NAME}^" 2>/dev/null || echo "") + if [ -z "$PREV_TAG" ]; then + RANGE="${GITHUB_REF_NAME}" + else + RANGE="${PREV_TAG}..${GITHUB_REF_NAME}" + fi + + { + echo "changelog<> "$GITHUB_OUTPUT" + shell: bash + + # ── Create GitHub Release ───────────────────────────── + - name: Create Release + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ github.ref_name }} + name: v${{ steps.tag.outputs.version }} + body: ${{ steps.changelog.outputs.changelog }} + draft: false + prerelease: false + generate_release_notes: false + files: | + onchain/target/wasm32-unknown-unknown/release/stellar_hunts.wasm + onchain/target/wasm32-unknown-unknown/release/stellar_hunts_nft.wasm + onchain/target/wasm32-unknown-unknown/release/stellar_hunts_receiver.wasm diff --git a/onchain/deny.toml b/onchain/deny.toml new file mode 100644 index 00000000..581fb58b --- /dev/null +++ b/onchain/deny.toml @@ -0,0 +1,77 @@ +# cargo-deny configuration +# See https://embarkstudios.github.io/cargo-deny/ for all options. + +[graph] +# Only check dependencies that are actually used (reachable from workspace crates). +# This excludes dev-dependencies and unused transitive deps. +# We keep dev-dependencies because tests are an important part of the CI surface. +exclude-dev = false + +[advisories] +# The path to the advisory database from which to fetch advisories. +db-path = "~/.cargo/advisory-db" +# URL to the advisory database to fetch. +db-urls = ["https://github.com/rustsec/advisory-db"] +# How often to fetch the advisory database (in seconds). +db-fetch-delay = 86400 # 24h +# If this is true, only advisories that have been marked as "informational" are +# allowed, all others will be denied. +vulnerability = "deny" +unmaintained = "warn" +unsound = "deny" +notice = "warn" +# Ignore specific advisories by ID (use sparingly, document the reason). +# Example: ignore = ["RUSTSEC-2023-0001"] +ignore = [] + +[licenses] +# Deny any crate that does not have a license we explicitly allow. +unlicensed = "deny" +# Allow these licenses (SPDX identifiers). +allow = [ + "MIT", + "Apache-2.0", + "Apache-2.0 WITH LLVM-exception", + "ISC", + "BSD-2-Clause", + "BSD-3-Clause", + "CC0-1.0", + "Unicode-DFS-2016", + "Unlicense", + "Zlib", +] +# Confidence threshold for license detection (0.0 – 1.0). +confidence-threshold = 0.8 +# Copyleft licenses are denied by default to keep the project MIT-friendly. +copyleft = "deny" +# If true, workspaces members are allowed to not have their licenses +# explicitly specified in their Cargo.toml (inheriting from workspace is OK). +allow-osi-fsf-free = "neither" +# Show which crates have copyleft licenses. +deny = [] +# Skip checking certain crates that we know have acceptable licensing but +# the detector can't determine automatically. +skip = [] +skip-tree = [] + +[bans] +# Multiple versions of the same crate are disallowed unless explicitly listed. +multiple-versions = "deny" +# Highlight crates that are specifically banned. +deny = [] +# Specific crates that are allowed despite having multiple versions. +skip-tree = [] +# Highlight crates with a specific git URL that should not be used. +deny = [] + +[sources] +# Unknown registries, git repos, and local paths may be denied. +unknown-registry = "deny" +unknown-git = "deny" +allow-git = [] +# Only allow crates from the official crates.io registry. +allowed-registries = ["https://github.com/rust-lang/crates.io-index"] + +[output] +# If true, output is printed in a format that is easier to read in CI logs. +feature-depth = 1