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
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:
306
justfile
306
justfile
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user