chore: migrate to v3, Doppler secrets, clean up legacy code
Some checks failed
CI / v3 / Check ui (pull_request) Failing after 15s
CI / v3 / Test backend (pull_request) Failing after 16s
CI / v3 / Docker / backend (pull_request) Has been skipped
CI / v3 / Docker / runner (pull_request) Has been skipped
CI / v3 / Docker / ui (pull_request) Has been skipped

- Remove all pre-v3 code: scraper, ui-v2, backend v1, ios v1+v2, legacy CI workflows
- Flatten v3/ contents to repo root
- Add Doppler secrets management (project=libnovel, config=prd)
- Add justfile with doppler run wrappers for all docker compose commands
- Strip hardcoded env fallbacks from docker-compose.yml
- Add minimal README.md
- Clean up .gitignore
This commit is contained in:
Admin
2026-03-23 17:21:12 +05:00
parent 1118392811
commit 59e8cdb19a
522 changed files with 5259 additions and 80365 deletions

306
justfile
View File

@@ -1,234 +1,118 @@
# justfile — libnovel-v2 task runner
# Install just: https://just.systems
# ── LibNovel v3 — justfile ────────────────────────────────────────────────────
# All commands that touch docker-compose are wrapped with `doppler run` so that
# secrets are injected into the environment at runtime — no .env files needed.
#
# Prerequisites:
# brew install doppler just
# doppler setup (run once; selects project=libnovel config=prd)
#
# Usage:
# just up # start all services (detached)
# just down # stop all services
# just logs # tail all service logs
# just ps # show running containers
# just build # rebuild backend + ui images
# just restart # full stop + start cycle
# just secrets # print all injected secrets (debug)
scraper_dir := "scraper"
ui_dir := "ui"
ios_dir := "ios/LibNovel"
ios_scheme := "LibNovel"
ios_sim := "platform=iOS Simulator,name=iPhone 17"
ios_spm := ".spm-cache"
runner_temp := env_var_or_default("RUNNER_TEMP", "/tmp")
set dotenv-load := false # Doppler handles all env; never load a .env file
# ─── Build ────────────────────────────────────────────────────────────────────
# ── Helpers ───────────────────────────────────────────────────────────────────
# Build the scraper binary
build:
cd {{scraper_dir}} && go build -o bin/scraper ./cmd/scraper
# Inject secrets from Doppler, then run the given command
doppler := "doppler run --"
# Build and verify all Go packages compile cleanly
build-all:
cd {{scraper_dir}} && go build ./...
# ── Core compose commands ─────────────────────────────────────────────────────
# ─── Tests ────────────────────────────────────────────────────────────────────
# Run unit tests only (no integration services required)
test:
cd {{scraper_dir}} && go test -race -count=1 -timeout=60s ./...
# Run integration tests (requires MinIO, PocketBase, optional Browserless)
# Override env vars as needed, e.g.:
# just test-integration MINIO_ENDPOINT=localhost:9000
test-integration:
cd {{scraper_dir}} && go test -v -tags integration -timeout 600s ./...
# Run unit + integration tests
test-all: test test-integration
# Run a specific package's integration tests, e.g.:
# just test-pkg internal/storage
test-pkg pkg:
cd {{scraper_dir}} && go test -v -tags integration -timeout 600s ./{{pkg}}/...
# Run end-to-end tests against live services.
# All services must be running first (docker compose up -d or just e2e-up).
# Override env vars as needed, e.g.:
# just test-e2e SCRAPER_URL=http://localhost:8080 KOKORO_VOICE=af_bella
test-e2e \
browserless_url="http://localhost:3030" \
minio_endpoint="localhost:9000" \
pocketbase_url="http://localhost:8090" \
scraper_url="http://localhost:8080":
cd {{scraper_dir}} && \
BROWSERLESS_URL={{browserless_url}} \
MINIO_ENDPOINT={{minio_endpoint}} \
POCKETBASE_URL={{pocketbase_url}} \
SCRAPER_URL={{scraper_url}} \
go test -v -tags integration -timeout 900s ./internal/e2e/...
# Start all services required for e2e tests, then run them
e2e: up test-e2e
# ─── Code quality ─────────────────────────────────────────────────────────────
# Run go vet on all packages (including integration build tag)
lint:
cd {{scraper_dir}} && go vet ./...
cd {{scraper_dir}} && go vet -tags integration ./...
# ─── UI ───────────────────────────────────────────────────────────────────────
# Type-check the SvelteKit UI
ui-check:
cd {{ui_dir}} && npx svelte-check
# Start the SvelteKit dev server
ui-dev:
cd {{ui_dir}} && npm run dev
# Install UI dependencies
ui-install:
cd {{ui_dir}} && npm install
# Build the UI for production
ui-build:
cd {{ui_dir}} && npm run build
# ─── iOS ──────────────────────────────────────────────────────────────────────
# Regenerate LibNovel.xcodeproj from project.yml (run after structural changes)
ios-gen:
cd {{ios_dir}} && xcodegen generate --spec project.yml --project .
# Resolve SPM package dependencies (cached to {{ios_spm}})
ios-resolve:
cd {{ios_dir}} && xcodebuild \
-project {{ios_scheme}}.xcodeproj \
-scheme {{ios_scheme}} \
-resolvePackageDependencies \
-clonedSourcePackagesDirPath {{ios_spm}}
# Build the iOS app for the simulator (no signing required)
# Runs ios-gen first to ensure the project is up to date.
ios-build: ios-gen ios-resolve
cd {{ios_dir}} && set -o pipefail && xcodebuild \
-project {{ios_scheme}}.xcodeproj \
-scheme {{ios_scheme}} \
-configuration Debug \
-destination 'generic/platform=iOS Simulator' \
-clonedSourcePackagesDirPath {{ios_spm}} \
CODE_SIGNING_ALLOWED=NO \
| xcpretty || xcodebuild \
-project {{ios_scheme}}.xcodeproj \
-scheme {{ios_scheme}} \
-configuration Debug \
-destination 'generic/platform=iOS Simulator' \
-clonedSourcePackagesDirPath {{ios_spm}} \
CODE_SIGNING_ALLOWED=NO
# Run unit tests on the simulator
# Runs ios-gen first to ensure the project is up to date.
ios-test: ios-gen ios-resolve
cd {{ios_dir}} && set -o pipefail && xcodebuild test \
-project {{ios_scheme}}.xcodeproj \
-scheme {{ios_scheme}} \
-configuration Debug \
-destination '{{ios_sim}}' \
-clonedSourcePackagesDirPath {{ios_spm}} \
CODE_SIGNING_ALLOWED=NO \
| xcpretty --report junit --output test-results.xml || true
# Archive a signed Release build (requires valid signing identity in keychain).
# Output: {{runner_temp}}/LibNovel.xcarchive
# Typically called from CI after importing certificate + provisioning profile.
# Usage: just ios-archive <team-id> <profile-uuid>
ios-archive team_id profile_uuid: ios-gen ios-resolve
cd {{ios_dir}} && xcodebuild archive \
-project {{ios_scheme}}.xcodeproj \
-scheme {{ios_scheme}} \
-configuration Release \
-destination 'generic/platform=iOS' \
-clonedSourcePackagesDirPath {{ios_spm}} \
-archivePath {{runner_temp}}/LibNovel.xcarchive \
CODE_SIGN_IDENTITY="Apple Distribution" \
"PROVISIONING_PROFILE[sdk=iphoneos*]={{profile_uuid}}" \
DEVELOPMENT_TEAM="{{team_id}}"
# Export an IPA from the archive produced by ios-archive.
# Requires ios/LibNovel/ExportOptions.plist.
# Output: {{runner_temp}}/ipa/LibNovel.ipa
ios-export:
cd {{ios_dir}} && xcodebuild -exportArchive \
-archivePath {{runner_temp}}/LibNovel.xcarchive \
-exportPath {{runner_temp}}/ipa \
-exportOptionsPlist ExportOptions.plist
# Set the build number (CFBundleVersion) in project.yml before archiving.
# Usage: just ios-set-build-number 42
ios-set-build-number number:
cd {{ios_dir}} && sed -i '' \
's/CURRENT_PROJECT_VERSION: .*/CURRENT_PROJECT_VERSION: {{number}}/' \
project.yml
# Upload the exported IPA to TestFlight via App Store Connect API.
# Requires env vars: ASC_KEY_ID, ASC_ISSUER_ID, ASC_PRIVATE_KEY_PATH
# The private key (.p8 file) must be present at ASC_PRIVATE_KEY_PATH.
ios-upload:
xcrun altool --upload-app \
--type ios \
--file {{runner_temp}}/ipa/LibNovel.ipa \
--apiKey "$ASC_KEY_ID" \
--apiIssuer "$ASC_ISSUER_ID"
# ─── Docker Compose ───────────────────────────────────────────────────────────
# Start all services (browserless, kokoro, scraper, minio, pocketbase)
# Start all services in the background
up:
docker compose up -d
{{doppler}} docker compose up -d
# Stop all services
# Start and stream logs (foreground)
up-fg:
{{doppler}} docker compose up
# Stop all running services
down:
docker compose down
{{doppler}} docker compose down
# Tail logs for all services
# Stop and remove volumes (full reset — destructive!)
down-volumes:
{{doppler}} docker compose down -v
# Show service status
ps:
{{doppler}} docker compose ps
# ── Build ─────────────────────────────────────────────────────────────────────
# Build (or rebuild) all images
build:
{{doppler}} docker compose build
# Build a specific service, e.g.: just build-svc backend
build-svc svc:
{{doppler}} docker compose build {{svc}}
# Pull latest base images
pull:
{{doppler}} docker compose pull
# ── Logs ─────────────────────────────────────────────────────────────────────
# Tail all service logs (last 50 lines + follow)
logs:
docker compose logs -f
{{doppler}} docker compose logs -f --tail=50
# Tail logs for a specific service, e.g.: just logs-service scraper
logs-service service:
docker compose logs -f {{service}}
# Tail a specific service, e.g.: just log backend
log svc:
{{doppler}} docker compose logs -f --tail=50 {{svc}}
# Rebuild and restart a specific service
restart service:
docker compose up -d --build {{service}}
# ── Lifecycle ─────────────────────────────────────────────────────────────────
# ─── Local dev: individual services ──────────────────────────────────────────
# Full restart: stop then start
restart: down up
# Start only PocketBase (for local storage testing)
pb-up:
docker compose up -d pocketbase
# Restart a single service, e.g.: just restart-svc backend
restart-svc svc:
{{doppler}} docker compose restart {{svc}}
# Start only MinIO (for local storage testing)
minio-up:
docker compose up -d minio
# Pull → build → recreate (rolling update without clearing volumes)
update:
{{doppler}} docker compose pull
{{doppler}} docker compose build
{{doppler}} docker compose up -d
# Start only Browserless (for local scraping tests)
browserless-up:
docker compose up -d browserless
# ── Initialisation ────────────────────────────────────────────────────────────
# Start storage backends only (MinIO + PocketBase)
storage-up:
docker compose up -d minio pocketbase
# Run one-shot init containers (minio-init, pb-init, postgres-init)
init:
{{doppler}} docker compose run --rm minio-init
{{doppler}} docker compose run --rm pb-init
{{doppler}} docker compose run --rm postgres-init
# ─── Convenience ─────────────────────────────────────────────────────────────
# ── Shell access ──────────────────────────────────────────────────────────────
# Show status of all docker compose services
status:
docker compose ps
# Open a shell in a running service, e.g.: just shell backend
shell svc:
{{doppler}} docker compose exec {{svc}} sh
# Remove all stopped containers and unused images
prune:
docker compose down --remove-orphans
docker image prune -f
# ── Secrets ───────────────────────────────────────────────────────────────────
# One-shot scrape of the full catalogue (requires services to be running)
scrape-run: build
cd {{scraper_dir}} && ./bin/scraper run
# Print all secrets Doppler will inject (never redirected to a file)
secrets:
doppler secrets --project libnovel --config prd
# One-shot scrape of a single book URL, e.g.:
# just scrape-book https://novelfire.net/book/my-novel
scrape-book url: build
cd {{scraper_dir}} && ./bin/scraper run --url {{url}}
# Print secrets as a .env-formatted list (useful for debugging)
secrets-env:
doppler secrets download --project libnovel --config prd --format env --no-file
# Start the HTTP server
serve: build
cd {{scraper_dir}} && ./bin/scraper serve
# Open Doppler dashboard in browser
secrets-dashboard:
doppler open dashboard
# ── Gitea CI ──────────────────────────────────────────────────────────────────
# Validate workflow files
ci-lint:
actionlint .gitea/workflows/*.yaml