v0.7.3 crawled for 35 minutes at the 6 GB cap and was killed without a
log. The container now gets 7 GB, the Gradle heap is 2 GB, the Metro
node process is capped at 1.5 GB and the job times out after 60 minutes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A hard reboot of the runner VM mid-build left broken transform metadata
in the persistent Gradle cache ("Could not read workspace metadata").
The APK step now detects it, drops the transforms cache and retries the
build once.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The APK job container now runs with --cpus=3 --memory=6g and no swap;
CMake / ninja for the React Native native code are limited to 2 jobs
(they used every core), and Gradle runs under nice / ionice. Notes for
hypervisor-hosted runners (Proxmox) are in docs/ci-cd.md.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- expo-router app in src/app: connect (server check, recent servers),
login, mandatory credential / password change, tabs Home / Search /
Collection / Profile, album and artist pages, full-screen player
- AuthSession per server with the refresh token in expo-secure-store
(Android Keystore); language from the profile
- player on react-native-track-player: ExoPlayer queue with a
notification and lock-screen controls via a playback service, 500 MB
stream cache, quality by network (Wi-Fi high, cellular medium),
play events with played time, reactions, playback state restore and
the endless My Wave with feedback
- home: pulsing wave circle driven by BPM / energy in the cover
palette, activity and mood chips that switch the wave on the fly
- New Architecture is off until react-native-track-player supports it;
the release build runs codegen only when it is on; query-string is a
direct dependency because expo-router 5.1 imports it undeclared
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With a container block act runs steps through sh, which rejected the bash
array in the build step (v0.1.13: Syntax error: "(" unexpected).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The app's CMake configure reads JNI sources generated by library codegen
(react-native-edge-to-edge), but the RN Gradle plugin does not declare that
dependency. With a warm Gradle cache configureCMake started before codegen
and v0.1.12 failed. Run generateCodegenArtifactsFromSchema in a separate
Gradle call first; both calls share one daemon.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
lintVital runs Google Play pre-release checks on every module (~1 min);
the APK is only published to Gitea releases.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Free space" no longer described them: the pre-build step now also prints
memory and the size of the persistent Gradle/Android caches, and the
failure step reports disk, memory and OOM kills.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The job reaches the Gitea API at its internal address, so html_url in the
response pointed at http://10.10.1.129:3000 and every link in the notes was
unreachable from outside. Build links from PUBLIC_URL (repo variable,
defaults to https://gitea.hsrv.site). A cancelled Android build now shows
"cancelled" instead of staying at "building".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ci.yml becomes ci-api, ci-contract, ci-recommender, ci-ts, ci-mobile and
docker workflows, each filtered by paths; docs-only commits run nothing
- docker builds all four images in one job with plain docker build on the
runner host, so the layer cache survives between runs
- release no longer re-runs the test suite (the tagged commit already passed
CI on main); version and release notes are one job
- the android job mounts persistent Gradle and Android SDK caches from
/opt/ci-cache (when the runner allows the volumes) and uses --build-cache
- RELEASE_TOKEN is optional: fall back to the built-in job token
- docs: CI layout and act_runner settings (capacity, cache, valid_volumes)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Successful in 51s
The release job now creates the Gitea release right after verification with
generated notes: Android build status, changes grouped by Conventional Commit
type with commit links, server update steps and APK install/check notes.
The android job attaches the APK and .sha256 and rewrites only the status
block (building / built with size, versionCode and SHA-256 / failed with a
log link). scripts/release.mjs is idempotent on re-runs and unit-tested.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Canceled after 0s
v0.1.5 got past Metro, but the Gradle daemon was killed mid-bundle on the
8 GB runner. Compile Kotlin inside the Gradle JVM instead of a second
daemon, limit Gradle and Metro to two workers, set the heap explicitly and
print free/dmesg when the build fails.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Successful in 53s
Expo's monorepo config watches all of apps/mobile, so during the release
build Metro crawled and hashed gigabytes of Gradle/CMake intermediates in
android/ and the createBundleReleaseJsAndAssets task appeared to hang.
Run the release build with CI=1 (no file watcher) and NODE_ENV=production.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Successful in 1m20s
The v0.1.2 release ran out of disk while Gradle unpacked the NDK; building
React Native's native code for four ABIs needs several more GB. Print df
before the build and after a failure to make the next shortage obvious.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Canceled after 0s
Security and robustness:
- storage-agent rejects an empty INTERNAL_TOKEN (config notEmpty + middleware)
- api takes the client IP only from X-Real-IP (chi RealIP trusted True-Client-IP)
- /readyz reports ok/fail and logs the cause; checks run in parallel with a timeout
- storage-agent shuts down gracefully; shared httpx.NewServer/Serve with IdleTimeout
- goose migrations take a Postgres advisory lock
- recommender readyz: per-check timeout, redis socket timeouts
Clients:
- Android release allows cleartext HTTP for LAN servers (expo-build-properties)
- checkServer times out after 10s; connect button is disabled while checking
Deploy and CI:
- install.sh hands cache/uploads/updates/models to the non-root service users
- update.sh refuses downgrades unless PIPU_ALLOW_DOWNGRADE=1
- recommender/analyzer get only the env they need; required secrets fail fast
- nginx security headers and CSP; strip True-Client-IP
- dev compose binds ports to 127.0.0.1; postgres/redis images pinned
- version.mjs fails on a malformed explicit tag instead of releasing 0.0.0-dev
- contract check catches untracked generated files
- final releases fail without a release keystore; changelog skips rc tags
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Successful in 50s
The default `tools` package no longer exists in the SDK repository, so
sdkmanager failed and the v0.1.1 release job never built the APK.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ci / docker (образы собираются — их сервер собирает сам при обновлении) (services/api/Dockerfile, storage-agent, storage-agent) (push) Successful in 52s
- deploy/compose.yaml: every app service has `build:` (repo root context),
local tags pipu-music-<service>:<version>
- deploy/update.sh: git fetch → checkout tag (latest release by default,
or vX.Y.Z / main) → docker compose build --pull → up -d → prune → /readyz;
re-executes from a temp copy because checkout may replace the script
- install.sh / .env.example: no REGISTRY, PIPU_VERSION set by update.sh,
.env owned by the invoking user
- release.yml: drop image publishing; release ships APK + sha256 only
- ci.yml: new `docker` job builds all images without push
- docs: install guide uses git clone, deployment/ci-cd updated, ADR-013
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- docs/install-guide.md: step-by-step server setup (DNS, Docker, install.sh,
.env, launch, checks), web and Android client setup, own reverse proxy
variant, updates, backups and troubleshooting
- deploy/compose.tls.yaml + Caddyfile: optional Caddy with automatic
Let's Encrypt in front of nginx (enabled via COMPOSE_FILE in .env)
- nginx: honour X-Forwarded-Proto/For from private networks
- .env.example: DOMAIN, ACME_EMAIL, COMPOSE_FILE, bootstrap admin vars
- install.sh: create caddy data dirs; release.yml: ship TLS files as assets
- deployment.md, docs index and README link to the guide
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>