kukuri build & deploy guide
===========================

Artifacts (all land in modules/orgflow-domain/build/distributions/):
  kukuri-desktop-<v>.zip          desktop fat jar + launcher   (bundleDesktop)
  kukuri-wasm-node-<v>.zip        Kotlin/Wasm node build       (bundleWasmNode)
  kukuri-wasm-web-<v>.zip         web app: index.html + orgflow-ui.js + webfont.js (bundleWeb)
  kukuri-native-linuxx64-<v>.zip  linux native executable      (bundleNative)
  kukuri-android-<v>.apk          debug-signed android APK     (bundleApk)

1. Local build
--------------
Requirements: JDK 21, Android SDK (ANDROID_HOME or local.properties), node.js (wasm tests).

Build everything:
  ./gradlew bundleDesktop bundleWasm bundleNative bundleWeb bundleApk

Single artifacts:
  ./gradlew :modules:orgflow-domain:bundleWeb
  ./gradlew :modules:orgflow-domain:bundleApk

Run the desktop shell:
  ./gradlew :apps:desktop:run

Test set (same steps CI runs):
  ./gradlew :modules:orgflow-domain:detekt assemble jvmTest \
      testDebugUnitTest testReleaseUnitTest wasmJsNodeTest linuxX64Test

2. git add -> buildbot (automatic build + deploy)
-------------------------------------------------
The buildbot master (http://192.168.3.110:8010) polls
http://192.168.3.110/git/kukuri.git (branch main) every 30 seconds.

  git add -A
  git commit -m "..."
  git push origin main          # origin pushes to both gitea and github

Within ~30s a build starts on the worker:
  setup-git-auth -> git checkout -> detekt-gate -> assemble
  -> jvm/android/wasm/native tests -> bundles
  -> wait-github-ios (blocks until the Actions "ios" run for the same commit
     finishes; fails the build if it fails)
  -> fetch-github-ios (downloads the "kukuri-ios" artifact, stages
     distributions/kukuri-ios-<v>.zip)
  -> deploy-to-lighttpd (FTP upload of all zips + the apk)

Watch progress:  http://192.168.3.110:8010  (waterfall / console view)
CLI watcher (waits for a build newer than the given number; exit 0 = success):
  ops/watch-buildbot.sh 32 900        # e.g. wait for build # > 32, up to 15 min

Manual trigger for a specific revision:
  curl -s -X POST http://192.168.3.110:8010/api/v2/forceschedulers/force \
    -H 'Content-Type: application/json' \
    -d '{"jsonrpc":"2.0","method":"force","id":1,"params":{"revision":"<sha>"}}'
(omit "revision" to build HEAD)

Deployed result:  http://192.168.3.110/artifact/kukuri/latest/
  web app:   http://192.168.3.110/artifact/kukuri/latest/web/index.html
  android:   http://192.168.3.110/artifact/kukuri/latest/kukuri-android-<v>.apk
  zips:      http://192.168.3.110/artifact/kukuri/latest/kukuri-*.zip

3. Changing the buildbot pipeline (deploy/buildbot/master/master.cfg)
---------------------------------------------------------------------
The repo copy deploy/buildbot/master/master.cfg is the source of truth. The
live master on 192.168.3.110 runs from /var/lib/buildbot/masters/default
(user "buildbot", system-wide /usr/bin/buildbot - the /opt/kukuri-buildbot dir
only keeps a spare copy of the cfg).

Apply (SSH password = ketsuban's git password, i.e. the worker's
/run/secrets/git_pass; sshpass keeps it one-shot):

  PW=$(docker exec kukuri-worker cat /run/secrets/git_pass)
  sshpass -p "$PW" scp deploy/buildbot/master/master.cfg \
      root@192.168.3.110:/var/lib/buildbot/masters/default/master.cfg
  sshpass -p "$PW" ssh root@192.168.3.110 \
      "chown buildbot:buildbot /var/lib/buildbot/masters/default/master.cfg && \
       buildbot reconfig /var/lib/buildbot/masters/default"

"Reconfiguration appears to have completed successfully" = ok, no restart
needed. Trigger a build to exercise the new pipeline (section 2).

4. Docker build test (CI parity before pushing)
-----------------------------------------------
The buildbot worker runs in a container ("kukuri-worker", built from
deploy/buildbot/worker/Dockerfile.worker) that already has JDK, Android SDK,
node and the shared gradle cache (GRADLE_USER_HOME=/var/jenkins_home/.gradle).
To verify exactly what CI will run before pushing, replay the committed tree
inside it:

  # 1. archive the committed tree (use /root: /tmp of the dev shell is not
  #    visible to docker cp)
  git archive main | gzip > /root/k.tgz
  docker cp /root/k.tgz kukuri-worker:/tmp/k.tgz

  # 2. replace the container's tree and run the full CI task list
  docker exec kukuri-worker bash -c '
    rm -rf /worker/kukuri-build/build &&
    mkdir -p /worker/kukuri-build/build &&
    tar xzf /tmp/k.tgz -C /worker/kukuri-build/build &&
    cd /worker/kukuri-build/build &&
    JAVA_HOME=/opt/java/openjdk GRADLE_USER_HOME=/var/jenkins_home/.gradle \
    ./gradlew :modules:orgflow-domain:detekt assemble jvmTest \
        testDebugUnitTest testReleaseUnitTest wasmJsNodeTest linuxX64Test \
        bundleDesktop bundleWasm bundleNative bundleWeb bundleApk'

Notes:
- Extracting a fresh archive (rm -rf + tar) guarantees no stale files from
  deleted sources linger - same as CI's clean checkout.
- The gradle caches persist in the container volume, so repeat runs are fast.
- For a fresh worker container on any host: deploy/buildbot/worker/run-worker.sh.

5. Error logs & diagnostics
---------------------------
buildbot (web UI):
  http://192.168.3.110:8010  -> builders -> kukuri-build -> click a build
  Each step shows "stdio"; failed steps show the tail inline (red).

buildbot (API, same data as the UI):
  steps of a build:      curl -s http://192.168.3.110:8010/api/v2/builds/<BUILD>/steps
  logs of a step:        curl -s http://192.168.3.110:8010/api/v2/steps/<STEPID>/logs
  full step log (raw):   curl -s http://192.168.3.110:8010/api/v2/logs/<LOGID>/raw
  (use the step's stepid and the log's logid from the JSON responses)

worker-side gradle reports (inside the kukuri-worker container):
  detekt:        modules/orgflow-domain/build/reports/detekt/detekt.txt
  jvm tests:     <module>/build/reports/tests/jvmTest/index.html
  android tests: <module>/build/reports/tests/testDebugUnitTest/...
  config cache:  build/reports/configuration-cache/<...>/configuration-cache-report.html
  problems:      build/build/reports/problems/problems-report.html
  e.g.:
    docker exec kukuri-worker cat \
      /worker/kukuri-build/build/modules/orgflow-domain/build/reports/detekt/detekt.txt

local docker build test (section 4): when ./gradlew fails there, rerun with the
failing task alone and grep around the failure:
  docker exec kukuri-worker bash -c 'cd /worker/kukuri-build/build && \
    JAVA_HOME=/opt/java/openjdk GRADLE_USER_HOME=/var/jenkins_home/.gradle \
    ./gradlew --console=plain <task> 2>&1 | grep -B2 -A8 "What went wrong"'

github actions (ios):
  https://github.com/ketsuban152/kukuri/actions  -> workflow run -> job step log

6. iOS
------
Two places:
  - buildbot ships "kukuri-ios-<v>.zip" (frameworks + klibs from the Actions
    run) to http://192.168.3.110/artifact/kukuri/latest/ together with the
    other artifacts.
  - the raw "kukuri-ios" Actions artifact stays on
    https://github.com/ketsuban152/kukuri/actions

ios.yml uses actions/cache for ~/.gradle/caches, ~/.gradle/wrapper and ~/.konan
(key: ios-<os>-<hash of build scripts>) - the first run after a cache-key
change is cold, later runs reuse the Konan toolchain and task outputs.

The worker reads /run/secrets/github_token (a repo-scoped PAT) for the
artifact download; run-worker.sh mounts it from
/root/buildbot/secrets/github_token.
