Skip to content
Open
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
@@ -0,0 +1,51 @@
name: CI

on:
push:
branches: [main]
pull_request:

jobs:
ci:
name: CI (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18, 20]

steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 9

- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile --ignore-scripts

- name: Prepare SvelteKit
run: pnpm --filter @devcard/web run prepare

- name: Typecheck
run: pnpm -r --if-present run typecheck

- name: Lint
run: pnpm -r --if-present run lint

- name: Test
run: pnpm -r --if-present run test

- name: Upload coverage
uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-node-${{ matrix.node-version }}
path: |
**/coverage/
**/.nyc_output/
if-no-files-found: ignore
17 changes: 17 additions & 0 deletions .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
name: PR Title

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

permissions:
pull-requests: read

jobs:
validate:
name: Validate PR title
runs-on: ubuntu-latest
steps:
- uses: amannn/action-semantic-pull-request@v5
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
52 changes: 44 additions & 8 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

### Prerequisites

- **Node.js** >= 20
- **Node.js** >= 18
- **pnpm** >= 9
- **Docker** & Docker Compose
- **React Native** dev environment — follow the [official setup guide](https://reactnative.dev/docs/environment-setup)
Expand Down Expand Up @@ -65,11 +65,13 @@ The mobile app uses Jest:
pnpm --filter @devcard/mobile test
```
#### apps/web
Currently, the web app does not define a test script.
The web app has a no-op test script (no tests yet).

#### packages/shared
The shared package does not include test scripts. It only provides linting and type checking.

The shared package uses Vitest:
```bash
pnpm --filter @devcard/shared test
```

## Project Structure

Expand All @@ -81,6 +83,41 @@ devcard/
└── packages/shared/ # Shared types, utils, platform registry
```

## CI / Pipeline

Every push to `main` and every pull request triggers the CI pipeline defined in [`.github/workflows/ci.yml`](.github/workflows/ci.yml).

The pipeline runs on Node.js 18 and 20 and executes these checks across all workspace packages:

| Step | Command |
|------|---------|
| Install | `pnpm install --frozen-lockfile` |
| Type check | `pnpm -r --if-present run typecheck` |
| Lint | `pnpm -r --if-present run lint` |
| Test | `pnpm -r --if-present run test` |
| Upload coverage | Artifacts uploaded from `**/coverage/` |

Before opening a PR, make sure these all pass locally:

```bash
pnpm -r --if-present run typecheck
pnpm -r --if-present run lint
pnpm -r --if-present run test
```

### PR Title Requirement

PR titles are validated against the [Conventional Commits](https://www.conventionalcommits.org/) spec by [`.github/workflows/pr-title.yml`](.github/workflows/pr-title.yml).

Your PR title **must** start with one of these prefixes:

`feat:`, `fix:`, `docs:`, `style:`, `refactor:`, `perf:`, `test:`, `build:`, `ci:`, `chore:`, `revert:`

Examples of valid PR titles:
- `feat: add QR code sharing screen`
- `fix: correct card deletion 404 response`
- `chore: update dependencies`

## Coding Standards

- **TypeScript** for all new code
Expand All @@ -92,10 +129,9 @@ devcard/

1. Create a feature branch from `main`: `git checkout -b feat/your-feature`
2. Make your changes with clear, descriptive commits
3. Ensure all tests pass: `pnpm test`
4. Ensure linting passes: `pnpm lint`
5. Open a PR against `main` with a clear description of the change
6. Wait for review — maintainers will respond within 48 hours
3. Ensure all checks pass locally (see [CI / Pipeline](#ci--pipeline) above)
4. Open a PR against `main` — the title must follow Conventional Commits format
5. Wait for review — maintainers will respond within 48 hours

## Reporting Issues

Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
<p align="center"><strong>One Tap. Every Profile. Every Platform.</strong></p>
<p align="center">Open Source Developer Profile Exchange Platform</p>
<p align="center">
<a href="https://github.com/Dev-Card/DevCard/actions/workflows/ci.yml">
<img src="https://github.com/Dev-Card/DevCard/actions/workflows/ci.yml/badge.svg" alt="CI" />
</a>
<a href="https://github.com/Dev-Card/DevCard">
<img src="https://img.shields.io/badge/GitHub-Dev--Card%2FDevCard-blue?logo=github&style=flat-square" alt="GitHub Repo" />
</a>
Expand Down Expand Up @@ -52,7 +55,7 @@ Each exchange is manual, error-prone, and slow. DevCard fixes this.

### Prerequisites

- Node.js >= 20
- Node.js >= 18
- pnpm >= 9
- Docker & Docker Compose
- React Native development environment ([setup guide](https://reactnative.dev/docs/environment-setup))
Expand Down Expand Up @@ -248,7 +251,6 @@ The following error cases are implemented:
| Scenario | Status | Response |
|----------|--------|----------|
| **Create/Update Card** | 400 | `{ error: 'Validation failed', details: parsed.error.flatten() }` — when title or linkIds don't meet constraints |
| **Create/Update Card** | 409 | `{ error: 'Username already taken'}` — when a user with the same username exists |
| **Update Card** | 404 | `{ error: 'Card not found' }` — when card ID doesn't exist or doesn't belong to authenticated user |
| **Delete Card** | 404 | `{ error: 'Card not found' }` — when card ID doesn't exist or doesn't belong to authenticated user |
| **Set Default Card** | 404 | `{ error: 'Card not found' }` — when card ID doesn't exist or doesn't belong to authenticated user |
Expand Down
12 changes: 12 additions & 0 deletions apps/backend/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import js from '@eslint/js';

export default [
js.configs.recommended,
{
files: ['src/**/*.ts'],
rules: {},
},
{
ignores: ['dist/**', 'node_modules/**'],
},
];
3 changes: 3 additions & 0 deletions apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"start": "node dist/server.js",
"test": "vitest run",
"test:watch": "vitest",
"typecheck": "tsc --noEmit",
"lint": "eslint src/",
"db:migrate": "prisma migrate dev",
"db:deploy": "prisma migrate deploy",
Expand All @@ -32,8 +33,10 @@
"zod": "^3.23.0"
},
"devDependencies": {
"@eslint/js": "^9.0.0",
"@types/node": "^22.0.0",
"@types/qrcode": "^1.5.0",
"eslint": "^9.0.0",
"pino-pretty": "^13.1.3",
"prisma": "^6.0.0",
"tsx": "^4.0.0",
Expand Down
7 changes: 4 additions & 3 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
"scripts": {
"android": "react-native run-android",
"ios": "react-native run-ios",
"typecheck": "tsc --noEmit",
"lint": "eslint .",
"start": "react-native start",
"test": "jest"
"test": "jest --passWithNoTests --forceExit"
},
"dependencies": {
"@devcard/shared": "workspace:*",
Expand Down Expand Up @@ -54,6 +55,6 @@
"typescript": "^5.8.3"
},
"engines": {
"node": ">= 22.11.0"
"node": ">= 18.0.0"
}
}
}
10 changes: 10 additions & 0 deletions apps/web/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import js from '@eslint/js';
import svelte from 'eslint-plugin-svelte';

export default [
js.configs.recommended,
...svelte.configs['flat/recommended'],
{
ignores: ['.svelte-kit/**', 'build/**', 'node_modules/**'],
},
];
8 changes: 7 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,22 @@
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync || echo ''",
"typecheck": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"lint": "eslint src/",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test": "exit 0"
},
"dependencies": {
"@devcard/shared": "workspace:*"
},
"devDependencies": {
"@eslint/js": "^9.0.0",
"@sveltejs/adapter-auto": "^7.0.0",
"@sveltejs/kit": "^2.50.2",
"@sveltejs/vite-plugin-svelte": "^6.2.4",
"eslint": "^9.0.0",
"eslint-plugin-svelte": "^2.46.1",
"svelte": "^5.51.0",
"svelte-check": "^4.4.2",
"typescript": "^5.9.3",
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
"web": "pnpm --filter @devcard/web dev"
},
"engines": {
"node": ">=20.0.0",
"node": ">=18.0.0",
"pnpm": ">=9.0.0"
},
"packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be",
"devDependencies": {
"concurrently": "^9.2.1"
}
}
}
12 changes: 12 additions & 0 deletions packages/shared/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import js from '@eslint/js';

export default [
js.configs.recommended,
{
files: ['src/**/*.ts'],
rules: {},
},
{
ignores: ['dist/**', 'node_modules/**'],
},
];
2 changes: 2 additions & 0 deletions packages/shared/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"test": "vitest run"
},
"devDependencies": {
"@eslint/js": "^9.0.0",
"eslint": "^9.0.0",
"typescript": "^5.4.0",
"vitest": "^2.0.0"
}
Expand Down