fix: align common behaviour with GitHub's runner (#1413)

Closes differences from GitHub's runner that common workflows hit:

- `ACTIONS_STEP_DEBUG` set as a repository variable enables debug output as on GitHub, where a secret of the same name still takes precedence. `runner.debug` and `RUNNER_DEBUG` are now also set inside composite actions, which lost the secret.
- `github.env` and `github.path` hold the file-command paths instead of being empty, so `echo "A=1" >> "${{ github.env }}"` works.
- Remote composite actions only get a pre stage when they nest actions. That stage evaluated `with:` before earlier steps ran, so an input like `${{ fromJSON(steps.meta.outputs.json).v }}` failed the job.
- `ports` on a job container are published, they were ignored while service ports worked.
- Dockerfile action build output reaches the job log. It was logged at debug and dropped, so a failing `RUN` showed only the final error.
- Local Docker actions honor their `.dockerignore`, as `docker build` does.
- Cancelling or timing out a host-mode step sends SIGINT, then SIGTERM after 7.5 seconds and SIGKILL after 2.5 more, as GitHub's runner does, so `trap` handlers and shutdown hooks run.
- `.sh` job hooks fall back to `sh` where bash is missing, and `.ps1` hooks to `powershell` where pwsh is missing, as on GitHub.

_Written by Claude (Opus 5.5)._

Reviewed-on: https://gitea.com/gitea/runner/pulls/1413
Reviewed-by: bircni <[email protected]>
Co-authored-by: silverwind <[email protected]>
This commit is contained in:
silverwind authored and bircni committed 2026-11-12 15:28:47 +00:00
1 parent 0dfec377d2
commit 645b17de72
25 files changed
+340 -91

No files matched your search

+33
View File
@@ -9,6 +9,7 @@ import (
"errors"
"os"
"syscall"
"time"
)
// Killer terminates a started process together with its whole process group,
@@ -52,5 +53,37 @@ func (k *Killer) Kill() error {
return nil
}
func (k *Killer) cancel(process *os.Process, graceful bool) error {
if graceful && k.pgid > 0 {
for _, stage := range []struct {
signal syscall.Signal
delay time.Duration
}{
{syscall.SIGINT, 7500 * time.Millisecond},
{syscall.SIGTERM, 2500 * time.Millisecond},
} {
if process.Signal(syscall.Signal(0)) != nil || syscall.Kill(-k.pgid, stage.signal) != nil || waitForExit(process, stage.delay) {
break
}
}
}
return k.Kill()
}
func waitForExit(process *os.Process, delay time.Duration) bool {
timer := time.NewTimer(delay)
defer timer.Stop()
ticker := time.NewTicker(10 * time.Millisecond)
defer ticker.Stop()
for !errors.Is(process.Signal(syscall.Signal(0)), os.ErrProcessDone) {
select {
case <-timer.C:
return false
case <-ticker.C:
}
}
return true
}
// Close is a no-op on Unix; there is no job handle to release.
func (k *Killer) Close() error { return nil }