# Gitea Actions CI

Workflow defined in [`build.yml`](./build.yml). Triggered on push / PR to
`dev` and `main`, or manually via `workflow_dispatch`.

## Jobs

| Job                  | Image                       | Role                                                                                     |
|----------------------|-----------------------------|------------------------------------------------------------------------------------------|
| `validate`           | `barichello/godot-ci:4.6`   | `godot --headless --import` then grep for `SCRIPT ERROR` / `Parse Error`. Uploads `.godot/` cache. |
| `lint`               | `ubuntu-latest` (Python)    | `gdlint scripts db scenes` via `gdtoolkit==4.*` (Scony). Parallel to `validate`; does not gate exports yet. |
| `export-desktop`     | `barichello/godot-ci:4.6`   | Matrix: Windows / Linux / macOS. Reuses the import cache, uploads each binary as artifact. |
| `export-android`     | `barichello/godot-ci:4.6` + JDK 17 + Android SDK installed at runtime | Provisions keystore, writes `editor_settings-4.tres` with SDK / JDK paths, exports APK. |

Artifacts are kept 14 days, accessible from the Gitea run page.

## Prerequisites before the first successful run

1. **Docker image tag** — verify `barichello/godot-ci:4.6` exists on Docker
   Hub. Otherwise adjust `GODOT_IMAGE` (common alternatives: `4.3`, `4.4`).
2. **macOS preset missing** — add it in Godot Editor → Project → Export →
   Add → macOS, name it exactly `macOS` (or change the matrix entry). The
   `.zip` will be unsigned; on Mac it needs
   `xattr -dr com.apple.quarantine` to launch.
3. **`Linux/X11Debug` preset** — Godot-3-era name. Reopen the project in
   Godot 4 once and re-save the preset (the editor may rename it). Update
   the matrix `preset:` field accordingly if it does.
4. **Gitea runner** — must support Docker containers (`act_runner` in
   `docker` mode, or `host` mode with Docker installed). Check
   `act_runner exec` access to Docker Hub.
5. **Optional secret** `ANDROID_KEYSTORE_BASE64` — `base64 -w0 debug.keystore`,
   stored as a Gitea repo secret. Without it, a throwaway keystore is
   generated per run, so the APK signature changes every build.

## Linting

`gdlint` (from Scony's `gdtoolkit`) runs in the `lint` job over `scripts/`,
`db/`, and `scenes/`. `addons/` (third-party LOD plugin) and `developers/`
(sandbox) are intentionally excluded.

The job is **non-blocking** today — the export jobs only depend on
`validate`, so a lint failure prints warnings but still produces binaries.
Once the codebase is clean, switch the export jobs' `needs: validate` to
`needs: [validate, lint]` to make lint a hard gate.

Suppress specific rules per-line with `# gdlint: disable=<rule>` or
project-wide with a `gdlintrc` file at the repo root (see
[gdtoolkit docs](https://github.com/Scony/godot-gdscript-toolkit/wiki)).

## Known issues from first runs

Captured from the first triggered runs on `feature/godot-migration`
(2026-05-17). Both must be resolved before the workflow can pass.

### 1. Container jobs fail at `actions/checkout@v4` with `node: not found`

```
OCI runtime exec failed: ... exec: "node": executable file not found in $PATH
```

`actions/checkout@v4` is a JavaScript action and needs Node.js inside
the container. The `barichello/godot-ci:4.6` image does not ship Node,
so every job using `container:` (validate, export-desktop, export-android)
breaks immediately.

Fix path (recommended): drop the `container:` block entirely and install
Godot in a step (the default `catthehacker/ubuntu:act-latest` image used
for `runs-on: ubuntu-latest` already has Node, Python, git, JDK). The
`lint` job is already structured this way.

Alternative: switch to a Godot CI image that bundles Node (some community
images do); pin a specific tag.

### 2. `actions/checkout` clones the wrong URL (resolved 2026-05-17)

The first runs failed at clone time because the runner asked for
`https://dev.stilobique.com/darknight/puzzle-quest/info/refs` while
Gitea was mounted under `/gitea/` behind YunoHost — the request was
intercepted by the YunoHost SSO at the root and redirected before
reaching Gitea.

Resolved by relocating Gitea to the root: it now serves at
`https://dev.stilobique.com/` directly (API at `/api/v1/...`,
`clone_url` at `/<owner>/<repo>.git`). The runner-injected
`GITHUB_SERVER_URL` and the actual Gitea base URL now agree.

If Gitea is ever moved back under a sub-path, the fix is `ROOT_URL` in
`app.ini` (`[server] ROOT_URL = https://<host>/<prefix>/`) or
re-registering `act_runner` with the full instance URL.

### 3. Default-branch mismatch

The Gitea API reports `default_branch: main` for the repo, but
`CLAUDE.md` describes `dev` as the default. The workflow listens to both,
so jobs trigger correctly either way, but the "Workflows" sidebar in the
Gitea UI reads from whatever the actual default branch is. If you intend
`dev` to be the default, update it under repo Settings → Branches.

## Differences from the old `.drone.yml`

- No more Drone, no more Butler — build only, artifacts downloadable from
  the Gitea UI.
- GDScript validation step before export (didn't exist).
- `.godot/` import cache shared between jobs (faster reruns).
- Keystore via Gitea secret instead of a public pCloud link.
- macOS target added (preset still to be created in Godot).
- `master` / empty `ReleaseVersion` pipeline → replaced by triggers on
  `main` (release branch per `CLAUDE.md`).

## Future: itch.io deploy via Butler

Not wired. When you want it back, add a `deploy-itch` job gated on tag
push (`v*`) that downloads the artifacts and runs
`butler push <dir> dev-crea/ahog:<channel>` with `BUTLER_API_KEY` from
secrets. Channels used historically:
`windows`, `linux`, `android`, `mac`.
