Starting two runner daemons with the same `.runner` file makes both present an
identical UUID+token, so Gitea treats them as one runner and they cancel each
other's jobs. This adds a non-blocking advisory lock on a sibling
`<runner-file>.lock`: the daemon (and `register`) acquire it at startup, and a
second process on the same host fails fast with a clear error instead of silently
interfering. The OS releases the lock when the process exits — including a hard
kill — so no stale lock is left behind. Legitimate multi-runner setups are
unaffected since each already uses its own `runner.file`.
Note: this covers the common single-host case; two hosts sharing a copied
`.runner` (e.g. over NFS) would still need server-side detection in Gitea.
---------
Co-authored-by: Zettat123 <zettat123@gmail.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/1099
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
An unchecked type assertion panics on input it did not expect, as a missing `tool_cache` key did in https://gitea.com/gitea/runner/pulls/1122.
Every flagged site is now handled where it can fail, or typed so it cannot: a `lock.Keyed` replaces the two `sync.Map` mutex registries, and the reporter's outputs carry an explicit sent flag. Mocks keep their assertions, a mismatch there is a setup error the panic names.
Bugs it turned up (only the first is reachable from workflows):
1. A scalar `matrix.include` or `matrix.exclude`, e.g. `include: foo` or `include: [1, 2]`, panicked the runner with `interface conversion: interface {} is string, not map[string]interface {}`. Verified against `main`, it is now a workflow error. `OnSchedule` panicked the same way on a malformed `on.schedule` entry.
1. `ExternalURL()` panicked on the nil listener after `Close()`, the port is now resolved once at startup.
1. `errors.Is(err, git.ErrShortRef)` followed by `err.(*git.Error)` panics as soon as anything wraps that error, so it is `errors.As` now.
1. An output name the server acknowledged without ever being sent one was recorded as sent forever, which silently dropped a later value for that name.
48576ab3e5 fixes one discovered issue: a matrix key holding a nested object was logged and then run as if the job had no matrix, so it now fails like an unknown `exclude` key.
Reviewed-on: https://gitea.com/gitea/runner/pulls/1123
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>