Skip to content

Commit 2f8d141

Browse files
authored
Merge pull request #6 from denvudd/chore/releases-and-ci
chore: added ci, commitlint, release workflows and cliff config
2 parents ec70c3c + 9c6c1ab commit 2f8d141

6 files changed

Lines changed: 261 additions & 0 deletions

File tree

.github/cliff.toml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[changelog]
2+
header = ""
3+
body = """
4+
{% for group, commits in commits | group_by(attribute="group") %}\
5+
### {{ group | upper_first }}
6+
7+
{% for commit in commits %}\
8+
- {% if commit.breaking %}[**breaking**] {% endif %}{{ commit.message }} ([`{{ commit.id | truncate(length=7, end="") }}`](https://github.com/denvudd/wishpicks/commit/{{ commit.id }}))
9+
{% endfor %}
10+
{% endfor %}
11+
"""
12+
trim = true
13+
14+
[git]
15+
conventional_commits = true
16+
filter_unconventional = true
17+
commit_parsers = [
18+
{ message = "^feat", group = "Features" },
19+
{ message = "^fix", group = "Bug Fixes" },
20+
{ message = "^perf", group = "Performance" },
21+
{ message = "^refactor", group = "Refactor" },
22+
{ message = "^docs", group = "Documentation" },
23+
{ message = "^chore|^ci|^build|^style|^test", group = "Miscellaneous" },
24+
{ message = "^revert", group = "Reverted" },
25+
]
26+
filter_commits = false
27+
tag_pattern = "v[0-9]*"
28+
topo_order = false
29+
sort_commits = "oldest"

.github/workflows/ci.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
7+
jobs:
8+
lint-api:
9+
name: Lint API
10+
runs-on: ubuntu-latest
11+
defaults:
12+
run:
13+
working-directory: apps/api
14+
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- name: Setup uv
19+
uses: astral-sh/setup-uv@v5
20+
with:
21+
enable-cache: true
22+
23+
- name: Install dependencies
24+
run: uv sync --dev
25+
26+
- name: Ruff lint
27+
run: uv run ruff check .
28+
29+
- name: Ruff format check
30+
run: uv run ruff format --check .
31+
32+
lint-web:
33+
name: Lint Web
34+
runs-on: ubuntu-latest
35+
36+
steps:
37+
- uses: actions/checkout@v4
38+
39+
- name: Setup Node
40+
uses: actions/setup-node@v4
41+
with:
42+
node-version: 22
43+
cache: npm
44+
cache-dependency-path: package-lock.json
45+
46+
- name: Install dependencies
47+
run: npm ci
48+
env:
49+
HUSKY: 0
50+
51+
- name: ESLint
52+
working-directory: apps/web
53+
run: npm run lint
54+
55+
- name: Typecheck
56+
working-directory: apps/web
57+
run: npx nuxt typecheck

.github/workflows/commitlint.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Commitlint
2+
3+
on:
4+
pull_request:
5+
branches: [main]
6+
7+
jobs:
8+
commitlint:
9+
name: Check commit messages
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- uses: actions/checkout@v4
14+
with:
15+
fetch-depth: 0
16+
17+
- name: Setup Node
18+
uses: actions/setup-node@v4
19+
with:
20+
node-version: 22
21+
cache: npm
22+
cache-dependency-path: package-lock.json
23+
24+
- name: Install dependencies
25+
run: npm ci
26+
env:
27+
HUSKY: 0
28+
29+
- name: Validate commit messages
30+
run: npx commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose

.github/workflows/release.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- "v[0-9]*.[0-9]*.[0-9]*"
7+
8+
permissions:
9+
contents: write
10+
11+
jobs:
12+
release:
13+
name: Create GitHub Release
14+
runs-on: ubuntu-latest
15+
16+
steps:
17+
- uses: actions/checkout@v4
18+
with:
19+
fetch-depth: 0
20+
21+
- name: Generate changelog
22+
id: cliff
23+
uses: orhun/git-cliff-action@v4
24+
with:
25+
config: .github/cliff.toml
26+
args: --latest --strip header
27+
28+
- name: Create GitHub Release
29+
env:
30+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
31+
run: |
32+
gh release create "${{ github.ref_name }}" \
33+
--title "Release ${{ github.ref_name }}" \
34+
--notes-file "${{ steps.cliff.outputs.changelog }}"

README.md

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
# Wishpicks
2+
3+
A wishlist platform. Users create gift wishlists and share links with friends. Friends can reserve items to avoid duplicate gifts — the wishlist owner never sees who reserved what.
4+
5+
## Stack
6+
7+
| Layer | Technology |
8+
|-------|-----------|
9+
| Frontend | Nuxt 3, Pinia, Tailwind CSS v4, Nuxt UI |
10+
| Backend | FastAPI, SQLAlchemy 2 (async), Alembic, Pydantic v2 |
11+
| Database | PostgreSQL (Neon in production) |
12+
| Cache / Rate limiting | Redis (Upstash in production) |
13+
| Media | Cloudinary |
14+
| Frontend hosting | Vercel |
15+
| Backend hosting | Railway / Render |
16+
17+
## Project structure
18+
19+
```
20+
wishpicks/
21+
├── apps/
22+
│ ├── web/ # Nuxt 3 frontend
23+
│ └── api/ # FastAPI backend
24+
├── .github/
25+
│ ├── workflows/ # CI/CD (see below)
26+
│ └── cliff.toml # Changelog format config
27+
├── .ai/ # Architecture docs, conventions
28+
├── docker-compose.yml
29+
└── .env.example
30+
```
31+
32+
## Getting started
33+
34+
**Prerequisites:** Docker, Docker Compose
35+
36+
```bash
37+
# 1. Copy env file and fill in the required values
38+
cp .env.example .env
39+
40+
# 2. Start all services (Postgres, Redis, API, Web)
41+
docker-compose up
42+
```
43+
44+
| Service | URL |
45+
|---------|-----|
46+
| Frontend | http://localhost:3000 |
47+
| Backend | http://localhost:8000 |
48+
| API docs | http://localhost:8000/docs |
49+
50+
## Environment variables
51+
52+
All variables are documented in `.env.example`. Required before first run:
53+
54+
Variables without values in `.env.example` are optional for local development (the features that depend on them won't work, but the app will start).
55+
56+
## Commit convention
57+
58+
This project uses [Conventional Commits](https://www.conventionalcommits.org). The format is enforced locally via Husky and in CI via the commitlint workflow.
59+
60+
```
61+
<type>(<scope>): <subject>
62+
63+
feat(api): add reservation endpoint
64+
fix(web): correct auth redirect on 401
65+
chore: update dependencies
66+
feat!: rename endpoint ← breaking change (! suffix)
67+
```
68+
69+
Allowed types: `feat`, `fix`, `chore`, `docs`, `refactor`, `test`, `style`, `perf`, `ci`, `build`, `revert`
70+
71+
Scope is optional and must be kebab-case when provided.
72+
73+
## CI/CD
74+
75+
### Workflows
76+
77+
**`ci.yml`** — runs on every PR to `main`
78+
79+
Runs two parallel jobs: `Lint API` (ruff check + format check) and `Lint Web` (eslint + nuxt typecheck). Both jobs always run regardless of which files changed.
80+
81+
**`commitlint.yml`** — runs on every PR to `main`
82+
83+
Validates that every commit title in the PR follows the Conventional Commits format. Fails the PR if any commit message is invalid.
84+
85+
**`release.yml`** — runs when a `v*.*.*` tag is pushed
86+
87+
Generates a changelog from commits since the previous tag using [git-cliff](https://github.com/orhun/git-cliff), then creates a GitHub Release with that changelog as the release notes.
88+
89+
### Creating a release
90+
91+
Decide the version bump based on what changed:
92+
93+
| Change type | Bump | Example |
94+
|-------------|------|---------|
95+
| Bug fixes only | patch | `v1.0.0``v1.0.1` |
96+
| New features, backward compatible | minor | `v1.0.0``v1.1.0` |
97+
| Breaking changes | major | `v1.0.0``v2.0.0` |
98+
99+
Then push a tag:
100+
101+
```bash
102+
git tag v1.1.0 -m "Release v1.1.0"
103+
git push origin v1.1.0
104+
```
105+
106+
GitHub Actions picks up the tag, generates the changelog automatically, and publishes the release. No manual steps needed beyond pushing the tag.

commitlint.config.cjs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
module.exports = {
22
extends: ['@commitlint/config-conventional'],
33
rules: {
4+
'type-enum': [2, 'always', [
5+
'feat', 'fix', 'chore', 'docs', 'refactor',
6+
'test', 'style', 'perf', 'ci', 'build', 'revert',
7+
]],
8+
'scope-case': [2, 'always', 'kebab-case'],
49
'subject-case': [2, 'never', ['sentence-case', 'start-case', 'pascal-case', 'upper-case']],
510
},
611
}

0 commit comments

Comments
 (0)