Reshapes the job log's "Set up job" section to mirror `actions/runner`:
- runner name/version, then `Runner Information` (labels, task, job, repository, event) and `Operating System` groups
- every required action downloaded up front under `Prepare all required actions`, each as `Download action repository '<action>@<ref>' (SHA:<sha>)`
- `Complete job name` closes the section
Downloading up front is the one behavioral change: the same set was already fetched during the pre stage regardless of a step's `if`, now just before the first pre step, so a download failure is reported against the job rather than a step.
---------
Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/1089
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
## Summary
A nested `uses:` action inside a **local composite action** fails to clone with a bare `authentication required: Unauthorized` (401) when the instance resolves host-less action references against itself (`DEFAULT_ACTIONS_URL = self`). The job fails at the composite **main→post boundary**, even though the composite's own steps and all post-steps report success.
## Root cause
`newCompositeRunContext` nils `Config.Secrets` so composite steps do not see job secrets. But the action-clone path in `prepareActionExecutor` sourced its token via `getGitCloneToken` → `Config.GetToken()` → `Config.Secrets`, which is empty inside a composite RunContext. The nested action is therefore cloned anonymously → 401 against the authenticated instance.
Two details explain the exact symptom:
- It surfaces at the composite **main→post boundary** because the swallowed nested-step error is re-emitted by `common.JobError` at the end of the composite main pipeline.
- It carries **no clone URL** because, on a warm action cache, only `r.Fetch` runs (not `PlainClone`), and the go-git fetch error is returned verbatim.
## Fix
Source the clone token from `github.Token` instead of `Config.Secrets`. It is preserved across the composite config copy (`Config.Token` / `PresetGitHubContext`) and is identical to `Config.GetToken()` at the top level, so top-level and `act exec` behaviour is unchanged. The `shouldCloneURLUseToken` host gate is kept so the token is never sent to a foreign host. This also aligns the git-clone path with the ActionCache fetch path, which already uses `github.Token`.
Reusable workflows are unaffected — their RunContext keeps `Config.Secrets`.
## Before / after
Local composite action with a nested `uses:` (+ post step), followed by a marker step. Same workflow, same runner host — only the runner fix differs.
| Job step | Before fix | After fix |
| --- | --- | --- |
| `Run actions/checkout@v6` | ✅ success | ✅ success |
| `Run ./.gitea/actions/probe-composite` (nested `uses:` + post) | ❌ **failure** — bare 401 at the main→post boundary | ✅ success |
| `Step after composite` | ⊘ **skipped** | ✅ **ran** |
| Job result | ❌ **failed** | ✅ **succeeded** |
### Before — boundary log
```text
::endgroup::
##[error]authentication required: Unauthorized ← bare 401, no clone target
Run Post ./.gitea/actions/probe-composite
Success - Post ./.gitea/actions/probe-composite ← post steps run and succeed
Success - Post actions/checkout@v6
Job failed
```
The composite's own steps and both post-steps report `Success`; the job fails solely on the bare 401 emitted at the main→post boundary, and the next step is skipped.
### After — boundary log
```text
Run ./.gitea/actions/probe-composite
...composite steps...
Success - ./.gitea/actions/probe-composite
Run Marker after composite
PASSED composite boundary — no 401 (runner fix confirmed)
Success - Marker after composite
Job succeeded
```
## Reproduction
`.gitea/actions/probe-composite/action.yml`:
```yaml
name: probe-composite
runs:
using: composite
steps:
- uses: actions/setup-node@v6 # nested uses → has a post step
with: { node-version: 22 }
- run: echo "composite inner step OK"
shell: bash
```
`.gitea/workflows/probe.yaml`:
```yaml
on: [push]
jobs:
composite-boundary:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: ./.gitea/actions/probe-composite
- run: echo "step after composite" # skipped before the fix; runs after
```
## Verification
- **Before:** bare 401 at the boundary, reproduced with a `delay=0` tail — rules out a token-TTL / expiry effect; it is a missing credential.
- **After:** the composite step succeeds, the step after the composite runs, the job succeeds, and there is no `authentication required` in the log.
- A reusable workflow (`workflow_call`) with the same nested `uses:` + post step never hit the 401, which isolated the bug to the composite main/post path.
## Tests
Adds `TestStepActionRemoteCloneTokenSurvivesNilSecrets` (`act/runner`): asserts the clone token is forwarded when the RunContext mirrors a composite (`Secrets == nil`, token via `Config.Token`), and that the host gate still withholds the token for a foreign host. Verified to fail without the fix and pass with it.
---------
Reviewed-on: https://gitea.com/gitea/runner/pulls/1041
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
Co-authored-by: Christian Heim <christian@heimdaheim.de>
Co-committed-by: Christian Heim <christian@heimdaheim.de>
Adds `ssh://` to the list of recognized URL schemes in `newRemoteAction`, so a
step can reference an action over SSH, e.g.:
```yaml
uses: ssh://git@gitea.example.com/actions/checkout@v4
```
Previously only `https://` / `http://` prefixes were parsed; an `ssh://` URL
fell through to the bare `org/repo` parser and failed.
### How auth works
SSH auth is delegated entirely to go-git's defaults — the runner configures no
SSH-specific options:
- **Which key?** go-git falls back to the host's **ssh-agent** (`$SSH_AUTH_SOCK`).
There is no key-file fallback, so the agent must hold a usable key. The SSH
**username** comes from the URL, so use `ssh://git@host/...` (a bare
`ssh://host/...` authenticates as an empty user and most servers reject it).
- **Host key trust?** Established out-of-band via the host's `known_hosts`
(`$SSH_KNOWN_HOSTS`, `~/.ssh/known_hosts`, `/etc/ssh/ssh_known_hosts`). The
runner host must already trust the remote; there is no accept-on-first-use.
- **Host key changes?** The clone fails with a host-key-mismatch error and stays
failed until `known_hosts` is updated on the host. Note `InsecureSkipTLS` does
**not** apply to SSH.
### Caching
The action cache path is derived from `{org}/{repo}` only (scheme/host are not
part of the key), so an `ssh://` action shares cache storage with the same
`org/repo` fetched over HTTP. This is unchanged by this PR and works in practice
(fetches resolve by SHA), but is worth noting.
### Tests
Adds `ssh://` cases to `Test_newRemoteAction` covering the scheme prefix, the
`git@` username placement, and a malformed-URL rejection. The agent/known_hosts
behavior lives in go-git and is not unit-tested here.
Fixes#841
Reviewed-on: https://gitea.com/gitea/runner/pulls/1035
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Removes 88 `nolint` directives (386 → 298) via mechanical, zero-regression cleanups:
- **38 `bodyclose`** in `act/artifactcache/handler_test.go`: replaced by `defer resp.Body.Close()` after each HTTP call.
- **21 dead directives** (`gocyclo`, `dogsled`, `contextcheck`): none of these linters are enabled in `.golangci.yml`, so the directives were doing nothing.
- **29 `testifylint`** directives whose underlying issues were addressed by mechanical rewrites:
- `assert.Nil(t, err)` → `assert.NoError(t, err)`
- `assert.NotNil(t, err)` → `assert.Error(t, err)`
- `assert.Equal(t, true/false, x)` → `assert.True/False(t, x)`
- `assert.Equal(t, 0, len(x))` → `assert.Empty(t, x)`
- `assert.Equal(t, N, len(x))` → `assert.Len(t, x, N)`
- `assert.Len(t, x, 0)` → `assert.Empty(t, x)`
Many `testifylint` directives still apply because they flag `require-error` (i.e. testifylint wants `require.NoError` instead of `assert.NoError` for early bail-out). That's a behavior change (fail-fast vs continue) and out of scope for this purely mechanical cleanup — those can be addressed in a follow-up. Same for `expected-actual`, `equal-values`, `error-is-as`, and the remaining `nilnil` / `unparam` / `forbidigo` / `staticcheck` / `goheader` / `dupl` directives.
`golangci-lint run` is clean. Tests pass for all touched packages.
---
This PR was written with the help of Claude Opus 4.7
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/864
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-committed-by: silverwind <2021+silverwind@noreply.gitea.com>
Merges the `gitea.com/gitea/act` fork into this repository as the `act/`
directory and consumes it as a local package. The `replace github.com/nektos/act
=> gitea.com/gitea/act` directive is removed; act's dependencies are merged
into the root `go.mod`.
- Imports rewritten: `github.com/nektos/act/pkg/...` → `gitea.com/gitea/act_runner/act/...`
(flattened — `pkg/` boundary dropped to match the layout forgejo-runner adopted).
- Dropped act's CLI (`cmd/`, `main.go`) and all upstream project files; kept
the library tree + `LICENSE`.
- Added `// Copyright <year> The Gitea Authors ...` / `// Copyright <year> nektos`
headers to 104 `.go` files.
- Pre-existing act lint violations annotated inline with
`//nolint:<linter> // pre-existing issue from nektos/act`.
`.golangci.yml` is unchanged vs `main`.
- Makefile test target: `-race -short` (matches forgejo-runner).
- Pre-existing integration test failures fixed: race in parallel executor
(atomic counters); TestSetupEnv / command_test / expression_test /
run_context_test updated to match gitea fork runtime; TestJobExecutor and
TestActionCache gated on `testing.Short()`.
Full `gitea/act` commit history is reachable via the second parent.
Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>