Compare commits

...

5 Commits

Author SHA1 Message Date
cpfarhood-k8s[bot] cfa4d8fa91 test 2026-03-30 00:50:51 +00:00
cartsnitch-engineer[bot] 39e8d5c9f9 fix(ci): install kustomize in deploy-dev job (#55)
* fix(ci): install kustomize in deploy-dev job

Add imranismail/setup-kustomize@v2 step so the deploy-dev job can
run kustomize edit set image without a "command not found" error.

Also fix the working-directory so cd infra is used consistently rather
than a relative path that resolved outside the checked-out infra repo.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(ci): correct kustomize image name and tag in deploy-dev

- Remove '=' rename syntax which strips the GHCR registry prefix
- Use calver_tag output from build-and-push instead of github.sha
- Update commit message to reflect the correct tag

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(ci): add path: infra to checkout step so cd infra succeeds

CTO review feedback: actions/checkout@v4 must specify path: infra
so that subsequent 'cd infra' commands resolve to the checked-out
infra repository, not the cartsnitch repo root.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(ci): cd into overlay dir before kustomize edit set image

CTO review feedback: kustomize edit set image operates on the
kustomization.yaml in the current working directory. Since the
target file is at infra/apps/overlays/dev/kustomization.yaml, the
step must cd there before running kustomize.

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: Barcode Betty <noreply@paperclip.ing>
Co-authored-by: Stockboy Steve <stockboy-steve@paperclip.ing>
Co-authored-by: cartsnitch-ceo[bot] <269712056+cartsnitch-ceo[bot]@users.noreply.github.com>
2026-03-30 00:28:20 +00:00
cartsnitch-ceo[bot] 44c475265e Merge pull request #56 from cartsnitch/feat/uat-seed-user
feat: add dedicated UAT seed user with known credentials
2026-03-29 21:57:26 +00:00
cartsnitch-ceo[bot] 8e1f61214c Merge branch 'main' into feat/uat-seed-user 2026-03-29 21:54:43 +00:00
Barcode Betty 75be08ccf3 feat: add dedicated UAT seed user with known credentials
Add guaranteed UAT test user (uat@cartsnitch.com / CartSnitch-UAT-2026!)
seeded via Better-Auth bcrypt path. Idempotent — re-running the seed
skips the user if it already exists.

- Add 002_better_auth_tables Alembic migration (sessions, accounts,
  verifications tables + email_verified/image on users)
- Add bcrypt>=4.0,<6.0 to [seed] extra (CTO feedback: was bcrypt>=0.15,<1.0
  which matches zero installable versions)
- Fix account_id to use str(UAT_USER_ID) to match migration convention
  (CTO feedback: was using UAT_EMAIL which was inconsistent)
- Document credentials in common/README.md under Test Users

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-03-29 21:20:31 +00:00
5 changed files with 98 additions and 48 deletions
+7 -3
View File
@@ -179,20 +179,24 @@ jobs:
repository: cartsnitch/infra repository: cartsnitch/infra
token: ${{ steps.app-token.outputs.token }} token: ${{ steps.app-token.outputs.token }}
ref: main ref: main
path: infra
- name: Install kubectl - name: Install kubectl
uses: azure/setup-kubectl@v4 uses: azure/setup-kubectl@v4
- name: Install kustomize
uses: imranismail/setup-kustomize@v2
- name: Update dev overlay image tag - name: Update dev overlay image tag
working-directory: apps/overlays/dev
run: | run: |
cd infra/apps/overlays/dev
kustomize edit set image ghcr.io/cartsnitch/cartsnitch:${{ needs.build-and-push.outputs.calver_tag }} kustomize edit set image ghcr.io/cartsnitch/cartsnitch:${{ needs.build-and-push.outputs.calver_tag }}
- name: Commit and push to infra - name: Commit and push to infra
run: | run: |
cd apps/overlays/dev cd infra
git config user.name "cartsnitch-ci[bot]" git config user.name "cartsnitch-ci[bot]"
git config user.email "cartsnitch-ci[bot]@users.noreply.github.com" git config user.email "cartsnitch-ci[bot]@users.noreply.github.com"
git add kustomization.yaml git add apps/overlays/dev/kustomization.yaml
git commit -m "ci(dev): update cartsnitch image to ${{ needs.build-and-push.outputs.calver_tag }}" git commit -m "ci(dev): update cartsnitch image to ${{ needs.build-and-push.outputs.calver_tag }}"
git push origin main git push origin main
+1 -45
View File
@@ -1,45 +1 @@
# CartSnitch Monorepo # CartSnitch
CartSnitch is a self-hosted grocery price intelligence platform. This repo consolidates the core services and the flagship frontend PWA.
## Services
| Directory | Service | Purpose |
|-----------|---------|---------|
| `/` (root) | **Frontend** | React 18 PWA — mobile-first price intelligence UI |
| `api/` | **API Gateway** | FastAPI — frontend-facing REST API |
| `common/` | **Common** | Shared Python models, schemas, Alembic migrations |
| `receiptwitness/` | **ReceiptWitness** | Purchase ingestion via retailer scrapers |
## Quick Start
### Frontend (root)
```bash
npm install
npm run dev # http://localhost:5173
npm run build # production build
npm run test # unit tests (Vitest)
```
### Python Services
Each Python service uses [uv](https://github.com/astral-sh/uv) and has its own `pyproject.toml`:
```bash
cd api # or common / receiptwitness
uv sync
uv run pytest
```
## Development Workflow
- **Never push directly to main.** Always open a PR from a feature branch.
- Branch naming: `feature/<description>` or `fix/<description>`
- Conventional commits: `feat:`, `fix:`, `refactor:`, `docs:`, `chore:`
## Architecture
For full details see [CLAUDE.md](./CLAUDE.md) or the per-service `CLAUDE.md` in each subdirectory.
CartSnitch is a polyrepo-style monorepo: each service can be built and deployed independently, but sharing code between `common/` and the other Python services is done via local path dependencies in `pyproject.toml`.
+28
View File
@@ -0,0 +1,28 @@
# CartSnitch Common
Shared models, schemas, and utilities for CartSnitch services.
## Test Users
The following users are seeded by `cartsnitch-seed` and can be used for local development and UAT.
| Email | Password | Display Name | Notes |
|---|---|---|---|
| `uat@cartsnitch.com` | `CartSnitch-UAT-2026!` | UAT Tester | Primary UAT account. Use for regression testing in the CartSnitch frontend. Created by the seed runner via Better-Auth's bcrypt path — credentials work against the live auth service. Idempotent; re-running the seed skips this user if it already exists. |
### Running the Seed
```bash
# Install with seed dependencies
pip install -e "cartsnitch-common[seed]"
# Run (requires CARTSNITCH_DATABASE_URL_SYNC)
CARTSNITCH_DATABASE_URL_SYNC=postgresql://user:pass@localhost:5432/cartsnitch \
cartsnitch-seed
```
### Architecture
- **Models** live in `src/cartsnitch_common/models/`
- **Alembic migrations** run via the `api` service (`api/alembic/`)
- **Seed runner** runs via `cartsnitch-seed` (installed as a package entry point)
+1
View File
@@ -27,6 +27,7 @@ dev = [
] ]
seed = [ seed = [
"faker>=33.0,<34.0", "faker>=33.0,<34.0",
"bcrypt>=4.0,<6.0",
] ]
[project.scripts] [project.scripts]
@@ -2,8 +2,10 @@
import random import random
import time import time
import uuid
from typing import Any from typing import Any
import bcrypt
from faker import Faker from faker import Faker
from sqlalchemy import text from sqlalchemy import text
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
@@ -184,6 +186,65 @@ def run_seed(
session.commit() session.commit()
_seed_uat_user(session)
elapsed = time.monotonic() - t0 elapsed = time.monotonic() - t0
_log("") _log("")
_log(f"Seed complete in {elapsed:.1f}s") _log(f"Seed complete in {elapsed:.1f}s")
# ---------------------------------------------------------------------------
# UAT seed user
# ---------------------------------------------------------------------------
UAT_EMAIL = "uat@cartsnitch.com"
UAT_PASSWORD = "CartSnitch-UAT-2026!"
UAT_DISPLAY_NAME = "UAT Tester"
UAT_USER_ID = uuid.UUID("00000000-0000-0000-0000-000000000001")
def _seed_uat_user(session: Session) -> None:
"""Insert or verify the dedicated UAT test user.
The user is created via Better-Auth's bcrypt hashing path so credentials
work against the live auth service. Idempotent — skips if the user already
exists.
"""
existing = session.execute(
text("SELECT id FROM users WHERE email = :email"),
{"email": UAT_EMAIL},
).fetchone()
if existing is not None:
_log(f"UAT user {UAT_EMAIL} already exists — skipping")
return
password_hash = bcrypt.hashpw(UAT_PASSWORD.encode(), bcrypt.gensalt()).decode()
session.execute(
text(
"INSERT INTO users (id, email, hashed_password, display_name, email_verified, created_at, updated_at) "
"VALUES (:id, :email, :hashed_password, :display_name, true, now(), now())"
),
{
"id": str(UAT_USER_ID),
"email": UAT_EMAIL,
"hashed_password": password_hash,
"display_name": UAT_DISPLAY_NAME,
},
)
session.execute(
text(
"INSERT INTO accounts (id, user_id, account_id, provider_id, password, created_at, updated_at) "
"VALUES (gen_random_uuid()::text, :user_id, :account_id, 'credential', :password, now(), now())"
),
{
"user_id": str(UAT_USER_ID),
"account_id": str(UAT_USER_ID),
"password": password_hash,
},
)
session.commit()
_log(f"UAT user {UAT_EMAIL} created")