Skip to content

Latest commit

 

History

History
286 lines (205 loc) · 12.3 KB

File metadata and controls

286 lines (205 loc) · 12.3 KB

SeedSync - Claude Code Instructions

Project Overview

SeedSync is a Docker-based tool that syncs files from a remote seedbox to a local machine using LFTP.

  • Frontend: Angular 21 (Bootstrap 5.3, Font Awesome 7, Vitest)
  • Backend: Python 3.13 with Bottle
  • Container: Multi-arch Docker image (amd64, arm64)
  • Registry: ghcr.io/nitrobass24/seedsync

Repository Structure

src/
├── angular/          # Angular 21 frontend
├── python/           # Python backend
├── docker/           # Docker build files
└── e2e-playwright/   # Playwright end-to-end tests
website/              # Docusaurus documentation site

Branches

  • master - Stable release branch
  • develop - Integration branch for all new work

Git Workflow

All work MUST follow this branching discipline:

  1. Always start from develop: Before writing any code, checkout develop and pull latest:
    git checkout develop && git pull origin develop
  2. Create a feature branch: Every task (feature, bugfix, refactor) gets its own branch off develop:
    git checkout -b feat/short-description   # or fix/short-description
  3. Commit only to the feature branch: Never commit directly to develop or master.
  4. One concern per branch: Do not mix unrelated changes into the same branch. If you discover a separate issue while working, finish or stash your current work, then create a new branch for the other issue.
  5. Open a PR to develop: When the work is complete, push the feature branch and open a PR targeting develop. Include a summary and test plan. Releases are the only path that PRs to master — see the Release Process below.
  6. Do not carry dirty working-tree changes across branches: Before switching branches, either commit or stash. Never rely on uncommitted edits surviving a git checkout.

Release Process

When merging a PR or completing significant work, follow this release process:

1. Update CHANGELOG.md

Add a new version entry at the top with:

  • Version number following semver (major.minor.patch)
  • Date in YYYY-MM-DD format
  • Sections: Changed, Added, Fixed, Removed, Security (as applicable)

Example:

## [0.10.0] - 2026-01-27

### Changed
- **Feature name** - Description of change

### Fixed
- **Bug name** - Description of fix

2. Update package.json Version

Update the version in src/angular/package.json to match the release version. This is displayed on the About page.

3. Create Release

Releases use a release/vX.Y.Z branch off develop, a PR into master, and a tag on the resulting merge commit. Never commit the release directly to develop or master.

# Branch from the latest develop
git checkout develop && git pull origin develop
git checkout -b release/vX.Y.Z

# Stage the release commit (CHANGELOG / package.json)
git add CHANGELOG.md src/angular/package.json
git commit -m "Release vX.Y.Z - Brief description"
git push -u origin release/vX.Y.Z

# Open a PR targeting master
gh pr create --base master --head release/vX.Y.Z \
  --title "Release vX.Y.Z - Brief description" \
  --body "Sync develop → master for vX.Y.Z. See CHANGELOG.md for details."

# After the PR is merged into master, fetch and tag the merge commit
git checkout master && git pull origin master
git tag vX.Y.Z
git push origin vX.Y.Z

The CI workflow will automatically:

  • Build multi-arch Docker images
  • Push to ghcr.io
  • Create GitHub release with auto-generated notes

4. Update GitHub Release Notes

After CI creates the release, update the release notes with detailed changelog:

gh release edit vX.Y.Z --repo nitrobass24/seedsync --notes "$(cat <<'EOF'
## What's Changed

### Fixed
- **Bug name** - Description of fix (#issue)

### Changed
- **Feature name** - Description of change

### Added
- **New feature** - Description

## Docker Pull

```bash
docker pull ghcr.io/nitrobass24/seedsync:X.Y.Z

Full Changelog: https://github.com/nitrobass24/seedsync/compare/vPREV...vX.Y.Z EOF )"


Format should match CHANGELOG.md entries with:
- Section headers: Fixed, Changed, Added, Removed, Security
- Bold feature/bug names
- Issue references where applicable
- Always include a "Docker Pull" section with the `docker pull` command for the release version

### 5. Verify Release

- Check GitHub Actions completed successfully
- Verify image is available: `docker pull ghcr.io/nitrobass24/seedsync:X.Y.Z`
- Verify GitHub release notes are formatted correctly

## Version Numbering

- **Major** (X.0.0): Breaking changes, major rewrites
- **Minor** (0.X.0): New features, significant improvements (e.g., Angular upgrade)
- **Patch** (0.0.X): Bug fixes, minor updates

## Key Files

| File | Purpose |
|------|---------|
| `CHANGELOG.md` | Release history and notes |
| `src/docker/build/docker-image/Dockerfile` | Multi-stage Docker build |
| `.github/workflows/ci.yml` | CI/CD pipeline |
| `.github/workflows/docs-pages.yml` | Documentation deployment |
| `website/` | Documentation site (Docusaurus) |

## CI/CD Workflows

### CI (`ci.yml`)

Triggers: `push` to `master` or `develop`, `pull_request` against `master` or `develop`, and `push` of a tag matching `v[0-9]+.[0-9]+.[0-9]+`.

Release-relevant matrix:

| Trigger | Build & Test | Publish Image | Create Release |
|---------|--------------|---------------|----------------|
| PR to master | ✅ | ❌ | ❌ |
| Push to master | ✅ | ❌ | ❌ |
| Push tag (`v*.*.*`) | ✅ | ✅ | ✅ |

- **Build & Test**: Builds Docker image and verifies container starts
- **Publish Image**: Pushes multi-arch image to ghcr.io (only on release tags)
- **Create Release**: Creates GitHub release with auto-generated notes (only on release tags)

### Docs (`docs-pages.yml`)

- Triggers only when `website/` directory changes
- Builds and deploys Docusaurus site to GitHub Pages

## Common Tasks

### Building Locally
```bash
make build    # Build Docker image
make run      # Run container
make logs     # View logs
make stop     # Stop container

Testing

cd src/angular && npx ng lint    # Angular ESLint
cd src/angular && npx ng test    # Angular Vitest unit tests
# Python tests run in Docker via CI (pytest in test-image)
# CI also runs: Docker build, container startup, web UI accessibility

Test Requirements

Every PR MUST include appropriate test changes. Tests are not optional — they ship with the code.

When adding a feature

  • Add unit tests for all new logic (services, components, utilities, handlers)
  • Cover the happy path, edge cases, and error/validation paths
  • If adding a new API endpoint, add integration tests using WebTest/TestApp

When fixing a bug

  • Add a test that reproduces the bug (fails before fix, passes after)
  • If the bug was in untested code, add baseline tests for the surrounding logic

When refactoring

  • Existing tests must continue to pass without modification (unless the refactor intentionally changes behavior)
  • If refactoring reveals untested code, add tests for it

When removing code

  • Remove or update tests that cover the deleted code
  • Do not leave dead test code behind

Test structure

Layer Framework Location Runner
Angular lint ESLint (angular-eslint) src/angular/eslint.config.js cd src/angular && npx ng lint
Angular unit tests Vitest src/angular/src/**/*.spec.ts cd src/angular && npx ng test
Python unit tests unittest src/python/tests/unittests/ pytest (in Docker test-image)
Python integration tests unittest + WebTest src/python/tests/integration/ pytest (in Docker test-image)

Test conventions

  • Angular: Use describe/it from Vitest. Mock services with vi.fn(). Use TestBed for component tests. Query DOM by text content or stable attributes, not positional selectors (nth-of-type).
  • Python: Use unittest.TestCase. Use unittest.mock.MagicMock/patch for mocking. Integration tests for web handlers use webtest.TestApp with BaseTestWebApp.
  • Naming: Test files mirror source files — foo.service.tsfoo.service.spec.ts, controller.pytest_controller.py.
  • No flaky tests: Avoid time.sleep in tests where possible. Use threading.Event or barriers for synchronization. Use timeout_decorator for tests that might hang.

Code Health

These are guidelines, not hard caps. Their real job is to slow you down and ask "is this still one concern?" Cross them when the code is clearer for it — flag in the PR body when you do.

Soft size targets

Unit Target When to pause
File ≤ 500 lines At 400, ask "one concern or several?"
Class ≤ 300 lines If state covers > 2 concerns, extract
Function / method ≤ 40 lines Longer is OK when linear; deep nesting is not
Cyclomatic complexity (per function) ≤ 12 Enforced in CI via ruff C901 (max-complexity = 12)

Cyclomatic complexity is the one number that actually predicts pain — a 200-line linear function is fine; a 40-line function with 5 nested conditionals is dangerous. Use ruff check --select C901 locally to measure.

The "before adding" rule

Before adding a method to an existing class, ask:

  1. Does this belong in a sibling module/class instead?
  2. Does the class already span multiple concerns (auth + persistence + validation, etc.)? If so, extracting first is usually worth the overhead.
  3. Prefer composition over accretion — reach for a new collaborator, not a bigger class.

Signals that you should split

  • A focused unit test needs cls.__new__(cls) or broad mocking just to exercise one method.
  • The constructor initializes unrelated state (e.g., both a model and a command queue and a logger facade).
  • One method dominates the file's line count.
  • Changes in separate features keep conflicting in the same file.

When you see these, open an issue for the extraction rather than working around them silently.

Enforcement

  • Qualitative checks: code review. Reviewers should push back on "this file is getting unwieldy" even without a hard rule.
  • Mechanical checks (Python): ruff's C901 (cyclomatic complexity) with max-complexity = 12. Enforced in CI; existing outliers are annotated with # noqa: C901. Test files (tests/**) are excluded via per-file-ignores in pyproject.toml.
  • Ratchet pattern: when a bound exists but the codebase has outliers, set the threshold just above the worst and drop it over time (same pattern we use for --max-warnings in ng lint).

Angular mutating-service contract

Services that mutate a BehaviorSubject from a REST/HTTP call should follow one documented contract so callers can reason about side effects:

  • Update the subject inside the returned observable's pipeline (tap/map), not via a second internal .subscribe(). A second internal subscribe fires the side effect independently of the caller, runs the request a second time when the source is cold, and makes "when does the store update?" caller-independent and surprising. Folding the mutation into the returned pipeline means the store updates exactly when (and as many times as) the caller subscribes.
  • Return a typed result and recover errors with catchError so the caller always gets a value (or a typed failure) rather than an error notification. Map HTTP responses to a small typed result/WebReaction.

IntegrationsService (src/angular/src/app/services/settings/integrations.service.ts) is the reference implementation: create/update/remove/test use tap/map to update instancesSubject and catchError to map failures to a typed result, with no second subscribe.

ConfigService.set and AutoQueueService.add/remove were migrated to this contract (#542): they now fold their BehaviorSubject mutation into the returned pipeline via tap (no second internal subscribe) and return the typed WebReaction (RestService.sendRequest already recovers HTTP errors into a failure WebReaction, so no extra catchError is needed). Their specs subscribe to the returned observable before asserting the subject mutated, since the mutation now defers to caller subscription. No remaining services use the legacy second-subscribe pattern.

GitHub Repository

  • Repo: github.com/nitrobass24/seedsync
  • Docs: nitrobass24.github.io/seedsync
  • Issues: Use for bug reports and feature requests