Commit Graph
5 Commits
Author SHA1 Message Date
timka123andClaude Opus 5 bc6ff3c489 ci: gitea actions for the suites, the image and the APK
Four workflows, none of which existed:

* `tests.yml` — backend, desktop, `:core` and the web app's typecheck+build, in
  parallel. `:core` needs no Android SDK at all, because settings.gradle.kts
  leaves `:app` out when there is none; `:app`'s Robolectric suite therefore
  lives in the APK workflow, where the SDK is installed anyway. Nothing needs a
  running backend — the suites feed on committed fixtures, which is the whole
  reason they were written that way.
* `lint.yml` — ruff, which the repo had been configured for and never run.
* `docker.yml` — builds the image, *starts* it against SQLite and waits for
  /api/health before pushing, then publishes `:latest` and `:<short-sha>` to
  git.timka.su. A tag nobody can run is worse than no tag.
* `android-release.yml` — the release APK on every push to master, signed from
  secrets, with `apksigner verify --print-certs` reporting which key was really
  used and failing if it was the debug one.

Three things the repo needed before any of that could work:

`versionCode` was pinned at 1, so every APK looked like the same version to the
phone and would not install over its predecessor. It now comes from the run
number, with `versionName` from a property, both via one `setting()` helper that
reads a Gradle property or an environment variable.

The release build fell back to the *debug* signing key whenever the keystore was
missing — silently. That is a trap: the APK installs, looks fine, and can never
be upgraded by a properly signed build. `KADENCE_REQUIRE_SIGNING=1` makes that
case fail the build instead, and the release job sets it whenever a keystore
secret is present.

The compose files built an image with no name, so there was nothing to pull.
`image:` now names the registry tag while `up --build` still builds locally.

Also: `android/ci/install-sdk.sh` (idempotent, cacheable, and the one place that
knows `platforms;android-37.0` is spelled with the minor version), the runner as
a compose service with its config and a setup checklist, six make targets, and a
README section. No runner is registered yet — `make ci-runner` is step two of
that checklist.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-22 17:33:27 +03:00
timka123andClaude Opus 5 5259e027c6 deploy: re-exec from a copy, so the script cannot rewrite itself mid-run
deploy.sh lives in the repo it updates. `git reset --hard` therefore replaced
the file bash was reading; bash keeps a byte offset into the script and resumed
at that offset in the new file, skipping whatever the diff had shifted.

That is how the previous commit's renewal hook ended up present on the server
and never executed: `grep` found it in deploy.sh, the hook directory was empty.

Stage one now updates the checkout and re-execs a copy under /tmp, which no
later `git reset` can touch; stage two does the work and removes the copy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-19 15:11:01 +03:00
timka123andClaude Opus 5 9270bd70cb deploy: reload nginx after a certificate renewal
`certbot certonly` replaces the files on disk but signals nothing, so nginx
would have gone on serving the old certificate until some unrelated reload
happened — which is to say the cert would have quietly expired in January.
`--nginx` installs this hook itself; keeping the vhost in git means we
install it ourselves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-19 15:04:08 +03:00
timka123andClaude Opus 5 eb49df58d7 deploy: http2 via the listen directive, for nginx 1.24
The server runs nginx 1.24.0; the standalone `http2 on;` directive only
arrived in 1.25.1, so `nginx -t` rejected the vhost as an unknown directive.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-19 15:00:13 +03:00
timka123andClaude Opus 5 f25ccdec38 deploy: production compose overlay, nginx vhost and deploy script
Three pieces so kadence.timka.su can be stood up and updated from the repo
rather than from shell history on the box:

* `docker-compose.prod.yml` — publishes the app on loopback only. The base
  file's `${APP_PORT}:8000` binds every interface, which on this host both
  collides with remnawave (already on 8000) and would serve the API beside
  the proxy without TLS. Compose *appends* merged port lists, so the override
  needs the `!override` tag or the public binding survives. uvicorn also gets
  `--proxy-headers`, without which every request looks like plain HTTP from
  127.0.0.1 and the session cookie loses its `Secure` flag.
* `deploy/nginx/kadence.timka.su.conf` — TLS, HSTS, the forwarded headers the
  cookie logic reads, and a 120s read timeout because one task refresh fans
  out to four third-party APIs.
* `deploy/deploy.sh` — idempotent install-or-update. Generates API_TOKEN and
  the database password once and never rotates them afterwards (that would
  lock out the phone and the desktop app), treats `.env.example`'s
  `change-me` placeholders as unset, and uses `certbot certonly --webroot`
  rather than `--nginx` so the vhost in git stays the truth.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-19 14:56:57 +03:00