Compare commits

..

582 Commits

Author SHA1 Message Date
Nicolas
c749e52bb7 fix(cleanup): kill Windows step process tree on cancel to avoid hang (#1011)
## Problem

Cancelling a job on a Windows host runner can leave the spawned process
tree running and hang the runner. When a step launches a shell that
starts a child which in turn spawns further GUI/background processes,
cancelling the job kills only the direct child (the default
`exec.CommandContext` behaviour). The surviving descendants inherited
the step's stdout/stderr pipe, so the read end never hit EOF and
`cmd.Wait()` blocked forever.

Because the step executor never returned:
- the orphaned processes kept running (the cancelled work was not
  actually stopped), and
- end-of-job cleanup (`Remove` → `terminateRunningProcesses`) was never
  reached, so the runner appeared to go offline / stop picking up jobs.

`CREATE_NEW_PROCESS_GROUP` does not help here — it affects Ctrl-C signal
delivery, not handle inheritance or tree termination.

## Fix

- Assign each Windows step process to a **Job Object** immediately after
  `cmd.Start()`. Descendants created afterwards are automatically part
  of the job.
- Override `cmd.Cancel` to `TerminateJobObject`, so cancellation kills
  the **entire descendant tree** atomically. This also closes the
  inherited pipe handles, so `cmd.Wait()` can return.
- Set `cmd.WaitDelay` (10s) as a safety net: once the process has
  exited, Wait force-closes the pipes and returns rather than blocking
  forever — covering the case where the job-object setup fails (e.g.
  nested-job restrictions), in which we fall back to the previous
  single-process kill.
- The Job Object is created **without** `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`,
  so closing the handle on normal completion does not kill legitimate
  background processes; the tree is only torn down on explicit cancel.

Implemented behind `runtime.GOOS == "windows"` with a Windows-only
`processKiller` (Job Object) and no-op stubs elsewhere, so non-Windows
behaviour (default cancellation + `Setpgid`) is unchanged.

## Changes

- `act/container/process_windows.go` — Job Object `processKiller`
  (create / assign / terminate).
- `act/container/process_other.go` — no-op stubs (`//go:build !windows`).
- `act/container/host_environment.go` — wire `cmd.Cancel` (tree kill)
  and `cmd.WaitDelay` into `exec()`.
- `go.mod` / `go.sum` — promote `golang.org/x/sys` to a direct
  dependency.

## Testing

I fully tested it already

## Notes

Follow-up to the Windows leftover-process reaping in #996: that sweep
now actually runs on cancellation because the step no longer hangs
before reaching it.

Reviewed-on: https://gitea.com/gitea/runner/pulls/1011
Reviewed-by: techknowlogick <9+techknowlogick@noreply.gitea.com>
2026-06-02 16:53:27 +00:00
silverwind
f17b6b9fc3 fix(container): re-validate cached container id before reuse (#1003)
`containerReference.id` was cached from `Create()` and never re-validated, so a container torn down out-of-band (AutoRemove on an unexpected exit, daemon-side cleanup, sibling-job race in a parallel matrix) left a stale id behind. The next `Copy`/`Exec` then hit the daemon with that dead id and failed the otherwise-successful job with `Could not find the file /var/run/act/ in container <id>`.

`find()` now `ContainerInspect`s the cached id and clears it only on a definitive `NotFound`; transient errors trust the cache so cleanup pipelines don't abort on a daemon blip. Operations that need a live container (`copyContent`/`copyDir`/`CopyTarStream`/`exec`/`GetContainerArchive`) fail fast with a clear `container "<name>" does not exist` instead of the daemon's generic empty-id error.

---
This PR was written with the help of Claude Opus 4.7

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/1003
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-committed-by: silverwind <2021+silverwind@noreply.gitea.com>
2026-05-29 22:33:44 +00:00
Christopher Homberger
c7c4bd600a fix: support multiline secret masking (#1001)
* command logging exposes multiline secrets more often than before
* duplicated add-mask command in reporter now handles this as well

Closes #998
Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/1001
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2026-05-29 19:58:15 +00:00
silverwind
abec931d98 fix: restore global docker config dir and socket env in tests (#1004)
`TestGetImagePullOptions` left docker/cli's process-global config dir pointed at `testdata/docker-pull-options` (which ships dummy `username:password` creds) via `config.SetDir`, without restoring it. Because that override is process-global, every later docker-gated test in the package then pulled with those creds — `TestDockerCopyToSymlinkPath`'s `alpine:latest` pull failed with `incorrect username or password` and broke CI. The workflow's `DOCKER_CONFIG` override can't mask this, since `SetDir` wins in-process.

Restore `config.Dir()` with `t.Cleanup`, and isolate the socket tests' leaks of the exported `CommonSocketLocations` and `DOCKER_HOST` behind an `isolateSocketEnv` helper.

Refs https://gitea.com/gitea/gitea.com/issues/83

---
This PR was written with the help of Claude Opus 4.8

Reviewed-on: https://gitea.com/gitea/runner/pulls/1004
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-29 16:47:10 +00:00
silverwind
270ea41232 fix: matrix-job data races + outputs, leaner offline test suite (#994)
Running the full suite under `-race` (dropping `-short`) exposed pre-existing data races in parallel matrix-job execution, fixed by not sharing mutable state across combinations:

- `containerDaemonSocket()`/`validVolumes()` derive per-job values instead of mutating shared `Config`
- `getWorkflowSecrets` builds a fresh map, `rc.steps()` clones each step, and go-git workdir access is serialized
- every write to a shared `Job`'s result/outputs runs under a per-`Job` lock, each combo interpolating outputs from a pristine snapshot (last wins, as on GitHub)

### Test suite

- capability gates (docker / network / host-tools / Linux) replace the `-short` skips, and the suite runs offline via local fixtures (the artifact flow uses an in-process loopback server, only the docker-action force-pull needs the network)
- drops redundant tests, adds a regression test for https://gitea.com/gitea/runner/issues/981 and a docker-in-docker harness (`make test-dind`)

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/994
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-29 05:23:10 +00:00
Nicolas
0b9f251b6a fix: deliver cancel ack and reap leftover Windows job processes (#996)
## Summary

- When Gitea cancels a job, the reporter cancels its own task context; the final Close() flush then aborted on that same cancelled context and Gitea never received the runner's acknowledgement (missing tail logs and final state).
- On Windows the cancelled context also neutralised terminateRunningProcesses, leaving step grandchildren alive in the workspace, holding file handles, so the runner could no longer clean up and pick up new work.
- Reporter.Close() now flushes on a detached, bounded context via a new rpcCtx() helper and configurable Runner.ReportCloseTimeout (default 10s).
- terminateRunningProcesses now PowerShell-enumerates Win32_Process and taskkill /T /F's every process whose ExecutablePath or CommandLine references the job's workspace directories, on a detached context.
- The daemon heartbeat loop still exits on <-r.ctx.Done(): the runner is intentionally seen as offline by Gitea during cleanup so it isn't handed a new task overlapping the in-progress teardown.

## Test plan

- [x] go test ./internal/pkg/report/... ./act/container/ -run 'TestReporter_ServerCancelStillFlushesFinal|TestBuildWindowsWorkspaceKillScript'
- [x] make fmt && make lint-go - 0 issues
- [x] GOOS=windows go build ./... - clean
- [x] Manual on a Windows runner: trigger a long-running workflow, cancel from Gitea UI; verify (a) the job ends with tail logs + cancelled state in Gitea, (b) workspace cleans up, (c) the runner picks up a new job without restart.

Authored-by: bircni
🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/996
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
2026-05-24 10:01:01 +00:00
Nicolas
273f6b4247 fix(reporter): respect configured log level for job log forwarding (#989)
## Summary

- Non-raw_output log entries above the globally configured `log.level` are no longer forwarded to the Gitea job log output
- Step output (`raw_output=true`) is always forwarded regardless of level — it is actual job stdout/stderr, not runner internals
- State-machine fields (`stepResult`, `jobResult`) are always processed regardless of level, preserving correct tracking for skipped steps (whose `stepResult` is emitted at `DebugLevel` in `step.go`)
- Extracts a `shouldAppendLogRow` helper to avoid repeating the combined `!duringSteps() && entry.Level <= log.GetLevel()` guard in three places

## Why not the approach in #677

PR #677 adds `if entry.Level != log.GetLevel() { return nil }` at the top of `Fire()`. That has two bugs:
1. Uses `!=` instead of `>`, so `Error`/`Fatal` entries are dropped when the configured level is `Warn`
2. Returns early before processing `stepResult`/`jobResult` state fields — skipped steps (whose `stepResult` is logged at `DebugLevel`) would never be marked complete

This fix instead applies the level guard only at the `r.logRows` append sites, leaving state tracking unconditional.

Relates to #409.

Reviewed-on: https://gitea.com/gitea/runner/pulls/989
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-05-23 17:28:44 +00:00
Renovate Bot
47ee45412a fix(deps): update module github.com/opencontainers/selinux to v1.15.0 (#990)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/opencontainers/selinux](https://github.com/opencontainers/selinux) | `v1.14.1` → `v1.15.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fopencontainers%2fselinux/v1.15.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fopencontainers%2fselinux/v1.14.1/v1.15.0?slim=true) |

---

### Release Notes

<details>
<summary>opencontainers/selinux (github.com/opencontainers/selinux)</summary>

### [`v1.15.0`](https://github.com/opencontainers/selinux/releases/tag/v1.15.0)

[Compare Source](https://github.com/opencontainers/selinux/compare/v1.14.1...v1.15.0)

This release adds a new function, SetProcessKind, which is to be used instead of KVMProcessLabel\[s] and InitProcessLabel\[s] in case the user only wants to change the type of the existing label, not generate a new one. It also fixes an CI issue and optimizes label.InitLabels for a few common cases.

#### What's Changed

- ci: set timeout for vm jobs by [@&#8203;kolyshkin](https://github.com/kolyshkin) in [#&#8203;270](https://github.com/opencontainers/selinux/pull/270)
- label.InitLabels: optimize by [@&#8203;kolyshkin](https://github.com/kolyshkin) in [#&#8203;269](https://github.com/opencontainers/selinux/pull/269)
- Add SetProcessKind by [@&#8203;kolyshkin](https://github.com/kolyshkin) in [#&#8203;271](https://github.com/opencontainers/selinux/pull/271)

**Full Changelog**: <https://github.com/opencontainers/selinux/compare/v1.14.1...v1.15.0>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE5MC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/990
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-22 07:10:19 +00:00
silverwind
38b69bb214 chore: pin Docker base images to explicit versions (#992)
Pin floating image tags:

- `golang` → `1.26-alpine3.23`
- `docker` dind variants → `29.5.2`
- `alpine` (basic stage + test fixture) → `3.23`

`ubuntu:24.04` and `scratch` left unchanged (no more-specific tag).

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/992
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-05-22 07:09:56 +00:00
Renovate Bot
1c62c0635f chore(deps): update actions/setup-node action to v6 (#991)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/setup-node](https://github.com/actions/setup-node) | action | major | `v4` → `v6` |

---

### Release Notes

<details>
<summary>actions/setup-node (actions/setup-node)</summary>

### [`v6.4.0`](https://github.com/actions/setup-node/releases/tag/v6.4.0)

[Compare Source](https://github.com/actions/setup-node/compare/v6.3.0...v6.4.0)

#### What's Changed

##### Dependency updates:

- Upgrade [@&#8203;actions](https://github.com/actions) dependencies by [@&#8203;Copilot](https://github.com/Copilot) in [#&#8203;1525](https://github.com/actions/setup-node/pull/1525)
- Update Node.js versions in versions.yml and bump package to v6.4.0  by [@&#8203;priya-kinthali](https://github.com/priya-kinthali) in [#&#8203;1533](https://github.com/actions/setup-node/pull/1533)

#### New Contributors

- [@&#8203;Copilot](https://github.com/Copilot) made their first contribution in [#&#8203;1525](https://github.com/actions/setup-node/pull/1525)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v6...v6.4.0>

### [`v6.3.0`](https://github.com/actions/setup-node/releases/tag/v6.3.0)

[Compare Source](https://github.com/actions/setup-node/compare/v6.2.0...v6.3.0)

#### What's Changed

##### Enhancements:

- Support parsing `devEngines` field by [@&#8203;susnux](https://github.com/susnux) in [#&#8203;1283](https://github.com/actions/setup-node/pull/1283)

> When using node-version-file: package.json, setup-node now prefers devEngines.runtime over engines.node.

##### Dependency updates:

- Fix npm audit issues by [@&#8203;gowridurgad](https://github.com/gowridurgad) in [#&#8203;1491](https://github.com/actions/setup-node/pull/1491)
- Replace uuid with crypto.randomUUID() by [@&#8203;trivikr](https://github.com/trivikr) in [#&#8203;1378](https://github.com/actions/setup-node/pull/1378)
- Upgrade minimatch from 3.1.2 to 3.1.5 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1498](https://github.com/actions/setup-node/pull/1498)

##### Bug fixes:

- Remove hardcoded bearer for mirror-url [@&#8203;marco-ippolito](https://github.com/marco-ippolito) in [#&#8203;1467](https://github.com/actions/setup-node/pull/1467)
- Scope test lockfiles by package manager and update cache tests by [@&#8203;gowridurgad](https://github.com/gowridurgad) in [#&#8203;1495](https://github.com/actions/setup-node/pull/1495)

#### New Contributors

- [@&#8203;susnux](https://github.com/susnux) made their first contribution in [#&#8203;1283](https://github.com/actions/setup-node/pull/1283)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v6...v6.3.0>

### [`v6.2.0`](https://github.com/actions/setup-node/releases/tag/v6.2.0)

[Compare Source](https://github.com/actions/setup-node/compare/v6.1.0...v6.2.0)

#### What's Changed

##### Documentation

- Documentation update related to absence of Lockfile by [@&#8203;mahabaleshwars](https://github.com/mahabaleshwars) in [#&#8203;1454](https://github.com/actions/setup-node/pull/1454)
- Correct mirror option typos by [@&#8203;MikeMcC399](https://github.com/MikeMcC399) in [#&#8203;1442](https://github.com/actions/setup-node/pull/1442)
- Readme update on checkout version v6 by [@&#8203;deining](https://github.com/deining) in [#&#8203;1446](https://github.com/actions/setup-node/pull/1446)
- Readme typo fixes [@&#8203;munyari](https://github.com/munyari) in [#&#8203;1226](https://github.com/actions/setup-node/pull/1226)
- Advanced document update on checkout version v6 by [@&#8203;aparnajyothi-y](https://github.com/aparnajyothi-y)  in [#&#8203;1468](https://github.com/actions/setup-node/pull/1468)

##### Dependency updates:

- Upgrade [@&#8203;actions/cache](https://github.com/actions/cache) to v5.0.1 by [@&#8203;salmanmkc](https://github.com/salmanmkc) in [#&#8203;1449](https://github.com/actions/setup-node/pull/1449)

#### New Contributors

- [@&#8203;mahabaleshwars](https://github.com/mahabaleshwars) made their first contribution in [#&#8203;1454](https://github.com/actions/setup-node/pull/1454)
- [@&#8203;MikeMcC399](https://github.com/MikeMcC399) made their first contribution in [#&#8203;1442](https://github.com/actions/setup-node/pull/1442)
- [@&#8203;deining](https://github.com/deining) made their first contribution in [#&#8203;1446](https://github.com/actions/setup-node/pull/1446)
- [@&#8203;munyari](https://github.com/munyari) made their first contribution in [#&#8203;1226](https://github.com/actions/setup-node/pull/1226)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v6...v6.2.0>

### [`v6.1.0`](https://github.com/actions/setup-node/releases/tag/v6.1.0)

[Compare Source](https://github.com/actions/setup-node/compare/v6...v6.1.0)

#### What's Changed

##### Enhancement:

- Remove always-auth configuration handling by [@&#8203;priyagupta108](https://github.com/priyagupta108) in [#&#8203;1436](https://github.com/actions/setup-node/pull/1436)

##### Dependency updates:

- Upgrade [@&#8203;actions/cache](https://github.com/actions/cache) from 4.0.3 to 4.1.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1384](https://github.com/actions/setup-node/pull/1384)
- Upgrade actions/checkout from 5 to 6 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1439](https://github.com/actions/setup-node/pull/1439)
- Upgrade js-yaml from 3.14.1 to 3.14.2 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1435](https://github.com/actions/setup-node/pull/1435)

##### Documentation update:

- Add example for restore-only cache in documentation by [@&#8203;aparnajyothi-y](https://github.com/aparnajyothi-y) in [#&#8203;1419](https://github.com/actions/setup-node/pull/1419)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v6...v6.1.0>

### [`v6.0.0`](https://github.com/actions/setup-node/releases/tag/v6.0.0)

[Compare Source](https://github.com/actions/setup-node/compare/v6...v6)

#### What's Changed

**Breaking Changes**

- Limit automatic caching to npm, update workflows and documentation by [@&#8203;priyagupta108](https://github.com/priyagupta108) in [#&#8203;1374](https://github.com/actions/setup-node/pull/1374)

**Dependency Upgrades**

- Upgrade ts-jest from 29.1.2 to 29.4.1 and document breaking changes in v5 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1336](https://github.com/actions/setup-node/pull/1336)
- Upgrade prettier from 2.8.8 to 3.6.2 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1334](https://github.com/actions/setup-node/pull/1334)
- Upgrade actions/publish-action from 0.3.0 to 0.4.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1362](https://github.com/actions/setup-node/pull/1362)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v5...v6.0.0>

### [`v6`](https://github.com/actions/setup-node/compare/v5.0.0...v6)

[Compare Source](https://github.com/actions/setup-node/compare/v5.0.0...v6)

### [`v5.0.0`](https://github.com/actions/setup-node/releases/tag/v5.0.0)

[Compare Source](https://github.com/actions/setup-node/compare/v5.0.0...v5.0.0)

#### What's Changed

##### Breaking Changes

- Enhance caching in setup-node with automatic package manager detection by [@&#8203;priya-kinthali](https://github.com/priya-kinthali) in [#&#8203;1348](https://github.com/actions/setup-node/pull/1348)

This update, introduces automatic caching when a valid `packageManager` field is present in your `package.json`. This aims to improve workflow performance and make dependency management more seamless.
To disable this automatic caching, set `package-manager-cache: false`

```yaml
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
  with:
    package-manager-cache: false
```

- Upgrade action to use node24 by [@&#8203;salmanmkc](https://github.com/salmanmkc) in [#&#8203;1325](https://github.com/actions/setup-node/pull/1325)

Make sure your runner is on version v2.327.1 or later to ensure compatibility with this release. [See Release Notes](https://github.com/actions/runner/releases/tag/v2.327.1)

##### Dependency Upgrades

- Upgrade [@&#8203;octokit/request-error](https://github.com/octokit/request-error) and [@&#8203;actions/github](https://github.com/actions/github) by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1227](https://github.com/actions/setup-node/pull/1227)
- Upgrade uuid from 9.0.1 to 11.1.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1273](https://github.com/actions/setup-node/pull/1273)
- Upgrade undici from 5.28.5 to 5.29.0 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1295](https://github.com/actions/setup-node/pull/1295)
- Upgrade form-data to bring in fix for critical vulnerability by [@&#8203;gowridurgad](https://github.com/gowridurgad) in [#&#8203;1332](https://github.com/actions/setup-node/pull/1332)
- Upgrade actions/checkout from 4 to 5 by [@&#8203;dependabot](https://github.com/dependabot)\[bot] in [#&#8203;1345](https://github.com/actions/setup-node/pull/1345)

#### New Contributors

- [@&#8203;priya-kinthali](https://github.com/priya-kinthali) made their first contribution in [#&#8203;1348](https://github.com/actions/setup-node/pull/1348)
- [@&#8203;salmanmkc](https://github.com/salmanmkc) made their first contribution in [#&#8203;1325](https://github.com/actions/setup-node/pull/1325)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v4...v5.0.0>

### [`v5`](https://github.com/actions/setup-node/compare/v4.4.0...v5.0.0)

[Compare Source](https://github.com/actions/setup-node/compare/v4.4.0...v5.0.0)

### [`v4.4.0`](https://github.com/actions/setup-node/releases/tag/v4.4.0)

[Compare Source](https://github.com/actions/setup-node/compare/v4.3.0...v4.4.0)

#### What's Changed

##### Bug fixes:

- Make eslint-compact matcher compatible with Stylelint by [@&#8203;FloEdelmann](https://github.com/FloEdelmann) in [#&#8203;98](https://github.com/actions/setup-node/pull/98)
- Add support for indented eslint output by [@&#8203;fregante](https://github.com/fregante) in [#&#8203;1245](https://github.com/actions/setup-node/pull/1245)

##### Enhancement:

- Support private mirrors by [@&#8203;marco-ippolito](https://github.com/marco-ippolito) in [#&#8203;1240](https://github.com/actions/setup-node/pull/1240)

##### Dependency update:

- Upgrade [@&#8203;action/cache](https://github.com/action/cache) from 4.0.2 to 4.0.3 by [@&#8203;aparnajyothi-y](https://github.com/aparnajyothi-y) in [#&#8203;1262](https://github.com/actions/setup-node/pull/1262)

#### New Contributors

- [@&#8203;FloEdelmann](https://github.com/FloEdelmann) made their first contribution in [#&#8203;98](https://github.com/actions/setup-node/pull/98)
- [@&#8203;fregante](https://github.com/fregante) made their first contribution in [#&#8203;1245](https://github.com/actions/setup-node/pull/1245)
- [@&#8203;marco-ippolito](https://github.com/marco-ippolito) made their first contribution in [#&#8203;1240](https://github.com/actions/setup-node/pull/1240)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v4...v4.4.0>

### [`v4.3.0`](https://github.com/actions/setup-node/releases/tag/v4.3.0)

[Compare Source](https://github.com/actions/setup-node/compare/v4.2.0...v4.3.0)

#### What's Changed

##### Dependency updates

- Upgrade [@&#8203;actions/glob](https://github.com/actions/glob) from 0.4.0 to 0.5.0 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1200](https://github.com/actions/setup-node/pull/1200)
- Upgrade [@&#8203;action/cache](https://github.com/action/cache) from 4.0.0 to 4.0.2 by [@&#8203;gowridurgad](https://github.com/gowridurgad) in [#&#8203;1251](https://github.com/actions/setup-node/pull/1251)
- Upgrade [@&#8203;vercel/ncc](https://github.com/vercel/ncc) from 0.38.1 to 0.38.3 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1203](https://github.com/actions/setup-node/pull/1203)
- Upgrade [@&#8203;actions/tool-cache](https://github.com/actions/tool-cache) from 2.0.1 to 2.0.2 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1220](https://github.com/actions/setup-node/pull/1220)

#### New Contributors

- [@&#8203;gowridurgad](https://github.com/gowridurgad) made their first contribution in [#&#8203;1251](https://github.com/actions/setup-node/pull/1251)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v4...v4.3.0>

### [`v4.2.0`](https://github.com/actions/setup-node/releases/tag/v4.2.0)

[Compare Source](https://github.com/actions/setup-node/compare/v4.1.0...v4.2.0)

#### What's Changed

- Enhance workflows and upgrade publish-actions from 0.2.2 to 0.3.0 by [@&#8203;aparnajyothi-y](https://github.com/aparnajyothi-y) in [#&#8203;1174](https://github.com/actions/setup-node/pull/1174)
- Add recommended permissions section to readme by [@&#8203;benwells](https://github.com/benwells) in [#&#8203;1193](https://github.com/actions/setup-node/pull/1193)
- Configure Dependabot settings by [@&#8203;HarithaVattikuti](https://github.com/HarithaVattikuti) in [#&#8203;1192](https://github.com/actions/setup-node/pull/1192)
- Upgrade `@actions/cache` to `^4.0.0` by [@&#8203;priyagupta108](https://github.com/priyagupta108) in [#&#8203;1191](https://github.com/actions/setup-node/pull/1191)
- Upgrade pnpm/action-setup from 2 to 4 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1194](https://github.com/actions/setup-node/pull/1194)
- Upgrade actions/publish-immutable-action from 0.0.3 to 0.0.4 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1195](https://github.com/actions/setup-node/pull/1195)
- Upgrade semver from 7.6.0 to 7.6.3 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1196](https://github.com/actions/setup-node/pull/1196)
- Upgrade [@&#8203;types/jest](https://github.com/types/jest) from 29.5.12 to 29.5.14 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1201](https://github.com/actions/setup-node/pull/1201)
- Upgrade undici from 5.28.4 to 5.28.5 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1205](https://github.com/actions/setup-node/pull/1205)

#### New Contributors

- [@&#8203;benwells](https://github.com/benwells) made their first contribution in [#&#8203;1193](https://github.com/actions/setup-node/pull/1193)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v4...v4.2.0>

### [`v4.1.0`](https://github.com/actions/setup-node/releases/tag/v4.1.0)

[Compare Source](https://github.com/actions/setup-node/compare/v4.0.4...v4.1.0)

#### What's Changed

- Resolve High Security Alerts by upgrading Dependencies by [@&#8203;aparnajyothi-y](https://github.com/aparnajyothi-y) in [#&#8203;1132](https://github.com/actions/setup-node/pull/1132)
- Upgrade IA Publish by [@&#8203;Jcambass](https://github.com/Jcambass) in [#&#8203;1134](https://github.com/actions/setup-node/pull/1134)
- Revise `isGhes` logic by [@&#8203;jww3](https://github.com/jww3) in [#&#8203;1148](https://github.com/actions/setup-node/pull/1148)
- Add architecture to cache key by [@&#8203;pengx17](https://github.com/pengx17) in [#&#8203;843](https://github.com/actions/setup-node/pull/843)
  This addresses issues with caching by adding the architecture (arch) to the cache key, ensuring that cache keys are accurate to prevent conflicts.
  Note: This change may break previous cache keys as they will no longer be compatible with the new format.

#### New Contributors

- [@&#8203;jww3](https://github.com/jww3) made their first contribution in [#&#8203;1148](https://github.com/actions/setup-node/pull/1148)
- [@&#8203;pengx17](https://github.com/pengx17) made their first contribution in [#&#8203;843](https://github.com/actions/setup-node/pull/843)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v4...v4.1.0>

### [`v4.0.4`](https://github.com/actions/setup-node/releases/tag/v4.0.4)

[Compare Source](https://github.com/actions/setup-node/compare/v4.0.3...v4.0.4)

#### What's Changed

- Add workflow file for publishing releases to immutable action package by [@&#8203;Jcambass](https://github.com/Jcambass) in [#&#8203;1125](https://github.com/actions/setup-node/pull/1125)
- Enhance Windows ARM64 Setup and Update micromatch Dependency by [@&#8203;priyagupta108](https://github.com/priyagupta108) in [#&#8203;1126](https://github.com/actions/setup-node/pull/1126)

##### Documentation changes:

- Documentation update in the README file by [@&#8203;suyashgaonkar](https://github.com/suyashgaonkar) in [#&#8203;1106](https://github.com/actions/setup-node/pull/1106)
- Correct invalid 'lts' version string reference by [@&#8203;fulldecent](https://github.com/fulldecent) in [#&#8203;1124](https://github.com/actions/setup-node/pull/1124)

#### New Contributors

- [@&#8203;suyashgaonkar](https://github.com/suyashgaonkar) made their first contribution in [#&#8203;1106](https://github.com/actions/setup-node/pull/1106)
- [@&#8203;priyagupta108](https://github.com/priyagupta108) made their first contribution in [#&#8203;1126](https://github.com/actions/setup-node/pull/1126)
- [@&#8203;Jcambass](https://github.com/Jcambass) made their first contribution in [#&#8203;1125](https://github.com/actions/setup-node/pull/1125)
- [@&#8203;fulldecent](https://github.com/fulldecent) made their first contribution in [#&#8203;1124](https://github.com/actions/setup-node/pull/1124)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v4...v4.0.4>

### [`v4.0.3`](https://github.com/actions/setup-node/releases/tag/v4.0.3)

[Compare Source](https://github.com/actions/setup-node/compare/v4.0.2...v4.0.3)

#### What's Changed

##### Bug fixes:

- Fix macos latest check failures by [@&#8203;HarithaVattikuti](https://github.com/HarithaVattikuti) in [#&#8203;1041](https://github.com/actions/setup-node/pull/1041)

##### Documentation changes:

- Documentation update to update default Node version to 20 by [@&#8203;bengreeley](https://github.com/bengreeley) in [#&#8203;949](https://github.com/actions/setup-node/pull/949)

##### Dependency  updates:

- Bump undici from 5.26.5 to 5.28.3 by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;965](https://github.com/actions/setup-node/pull/965)
- Bump braces from 3.0.2 to 3.0.3 and other dependency updates by [@&#8203;dependabot](https://github.com/dependabot) in [#&#8203;1087](https://github.com/actions/setup-node/pull/1087)

#### New Contributors

- [@&#8203;bengreeley](https://github.com/bengreeley) made their first contribution in [#&#8203;949](https://github.com/actions/setup-node/pull/949)
- [@&#8203;HarithaVattikuti](https://github.com/HarithaVattikuti) made their first contribution in [#&#8203;1041](https://github.com/actions/setup-node/pull/1041)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v4...v4.0.3>

### [`v4.0.2`](https://github.com/actions/setup-node/releases/tag/v4.0.2)

[Compare Source](https://github.com/actions/setup-node/compare/v4.0.1...v4.0.2)

#### What's Changed

- Add support for `volta.extends` by [@&#8203;ThisIsManta](https://github.com/ThisIsManta) in [#&#8203;921](https://github.com/actions/setup-node/pull/921)
- Add support for arm64 Windows by [@&#8203;dmitry-shibanov](https://github.com/dmitry-shibanov) in [#&#8203;927](https://github.com/actions/setup-node/pull/927)

#### New Contributors

- [@&#8203;ThisIsManta](https://github.com/ThisIsManta) made their first contribution in [#&#8203;921](https://github.com/actions/setup-node/pull/921)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v4.0.1...v4.0.2>

### [`v4.0.1`](https://github.com/actions/setup-node/releases/tag/v4.0.1)

[Compare Source](https://github.com/actions/setup-node/compare/v4...v4.0.1)

#### What's Changed

- Ignore engines in Yarn 1 e2e-cache tests by [@&#8203;trivikr](https://github.com/trivikr) in [#&#8203;882](https://github.com/actions/setup-node/pull/882)
- Update setup-node references in the README.md file to setup-node\@&#8203;v4 by [@&#8203;jwetzell](https://github.com/jwetzell) in [#&#8203;884](https://github.com/actions/setup-node/pull/884)
- Update reusable workflows to use Node.js v20 by [@&#8203;MaksimZhukov](https://github.com/MaksimZhukov) in [#&#8203;889](https://github.com/actions/setup-node/pull/889)
- Add fix for cache to resolve slow post action step by [@&#8203;aparnajyothi-y](https://github.com/aparnajyothi-y) in [#&#8203;917](https://github.com/actions/setup-node/pull/917)
- Fix README.md by [@&#8203;takayamaki](https://github.com/takayamaki) in [#&#8203;898](https://github.com/actions/setup-node/pull/898)
- Add `package.json` to `node-version-file` list of examples. by [@&#8203;TWiStErRob](https://github.com/TWiStErRob) in [#&#8203;879](https://github.com/actions/setup-node/pull/879)
- Fix node-version-file interprets entire package.json as a version by [@&#8203;NullVoxPopuli](https://github.com/NullVoxPopuli) in [#&#8203;865](https://github.com/actions/setup-node/pull/865)

#### New Contributors

- [@&#8203;trivikr](https://github.com/trivikr) made their first contribution in [#&#8203;882](https://github.com/actions/setup-node/pull/882)
- [@&#8203;jwetzell](https://github.com/jwetzell) made their first contribution in [#&#8203;884](https://github.com/actions/setup-node/pull/884)
- [@&#8203;aparnajyothi-y](https://github.com/aparnajyothi-y) made their first contribution in [#&#8203;917](https://github.com/actions/setup-node/pull/917)
- [@&#8203;takayamaki](https://github.com/takayamaki) made their first contribution in [#&#8203;898](https://github.com/actions/setup-node/pull/898)
- [@&#8203;TWiStErRob](https://github.com/TWiStErRob) made their first contribution in [#&#8203;879](https://github.com/actions/setup-node/pull/879)
- [@&#8203;NullVoxPopuli](https://github.com/NullVoxPopuli) made their first contribution in [#&#8203;865](https://github.com/actions/setup-node/pull/865)

**Full Changelog**: <https://github.com/actions/setup-node/compare/v4...v4.0.1>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xOTAuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE5MC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/991
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-22 06:28:26 +00:00
silverwind
0e0c54b272 test: make TestRunEvent integration suite runnable locally (#987)
The `TestRunEvent*` integration tests are skipped in CI (`make test` runs `-short`), which hid several breakages that make them fail when run locally:

- `runTest` built the runner `Config` without `ContainerMaxLifetime`, so the job container ran `/bin/sleep 0` and exited immediately — every step failed with "container is not running". Set it to 1h.
- The root `.gitignore`'s unscoped `.env` and `dist` rules shadowed fixtures under `testdata/`. Anchored `dist` → `/dist` (the goreleaser output) and un-ignored `testdata/secrets/.env`.
- Added the missing `testdata/secrets/.env` fixture for `TestRunEventSecrets`.
- The `node24` local action referenced a `dist/index.js` bundle that was never committed (and was gitignored). Made the fixture self-contained (dependency-free ESM, `main: index.js`) so it runs without an `ncc` build. If you'd rather keep the `@actions/core`-based action and commit the built bundle instead, happy to switch.

Network-dependent subtests (remote `uses:`/composite actions) are out of scope.

---
This PR was written with the help of Claude Opus 4.7

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/987
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-21 19:16:23 +00:00
Nicolas
d6fbe75721 ci: add PR title linting against Conventional Commits (#988)
Lint PR titles

Reviewed-on: https://gitea.com/gitea/runner/pulls/988
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-05-21 19:13:43 +00:00
silverwind
b30204aa94 fix: clean up job network and container when container start fails (#986)
The teardown that removes a job's per-job network and container runs as a `Finally` on the step pipeline in `newJobExecutor`, which only executes after a successful start. When the start itself fails (e.g. a `docker cp` error from a buggy daemon), that `Finally` is skipped, so the network and container leak until Docker's address pool is exhausted and later jobs can no longer create networks.

This tears them down in `startContainer` when the start returns an error, reusing the existing `cleanUpJobContainer` teardown.

Exposed by the daemon regression in https://gitea.com/gitea/runner/issues/981, where every failed `docker cp` leaked a per-job network.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/986
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-21 15:19:01 +00:00
Renovate Bot
7b5ebe9618 fix(deps): update module connectrpc.com/connect to v1.20.0 (#985)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [connectrpc.com/connect](https://github.com/connectrpc/connect-go) | `v1.19.2` → `v1.20.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/connectrpc.com%2fconnect/v1.20.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/connectrpc.com%2fconnect/v1.19.2/v1.20.0?slim=true) |

---

### Release Notes

<details>
<summary>connectrpc/connect-go (connectrpc.com/connect)</summary>

### [`v1.20.0`](https://github.com/connectrpc/connect-go/releases/tag/v1.20.0)

[Compare Source](https://github.com/connectrpc/connect-go/compare/v1.19.2...v1.20.0)

#### What's Changed

##### Other changes

- Bump minimum supported Go version to 1.25 by [@&#8203;jonbodner-buf](https://github.com/jonbodner-buf) in [#&#8203;922](https://github.com/connectrpc/connect-go/issues/922)
- Update Unary-Get query parameter order to match spec recommendation by [@&#8203;oliversun9](https://github.com/oliversun9) in [#&#8203;926](https://github.com/connectrpc/connect-go/issues/926)

#### New Contributors

- [@&#8203;jonbodner-buf](https://github.com/jonbodner-buf) made their first contribution in [#&#8203;922](https://github.com/connectrpc/connect-go/issues/922)

**Full Changelog**: <https://github.com/connectrpc/connect-go/compare/v1.19.2...v1.20.0>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xODYuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE4Ni4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/985
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-21 00:32:31 +00:00
Nicolas
4317662a38 update docker cli to v29.5.2 (#984)
Fixes #981

Reviewed-on: https://gitea.com/gitea/runner/pulls/984
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-committed-by: Nicolas <bircni@icloud.com>
2026-05-20 20:25:44 +00:00
Vi
2208e7ec63 feat: add cache.offline_mode to reuse cached actions (#966)
Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: TKaxv_7S <56359+tkaxv_7s@noreply.gitea.com>
Co-authored-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: TKaxv_7S <954067342@qq.com>
Co-authored-by: TKaxv_7S <tkaxv_7s@noreply.gitea.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/966
Reviewed-by: Nicolas <bircni@icloud.com>
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Vi <w11b@ya.ru>
Co-committed-by: Vi <w11b@ya.ru>
2026-05-20 14:09:39 +00:00
silverwind
fab9714f9a Remove stale Gitea 1.20 compatibility shims (#978)
The runner already enforces Gitea v1.21+ (see the `connect.CodeUnimplemented` check in `daemon.go`), so several shims kept for v1.20 compatibility have been dead since 2023:

- `compatibleWithOldEnvs` — the `GITEA_DEBUG`, `GITEA_TRACE`, `GITEA_RUNNER_CAPACITY`, `GITEA_RUNNER_FILE`, `GITEA_RUNNER_ENVIRON`, `GITEA_RUNNER_ENV_FILE` env vars (superseded by the config file)
- `VersionHeader` (`x-runner-version`) and the `version` param of `client.New`
- `AgentLabels` field in `RegisterRequest` (replaced by `Labels`)

Also replaces a verbose `strings.TrimRightFunc` closure with `strings.TrimRight(s, "\r\n")` in the log row parser.

---
This PR was written with the help of Claude Opus 4.7

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/978
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-19 16:49:15 +00:00
Renovate Bot
10475db58a fix(deps): update module github.com/docker/cli to v29.5.1+incompatible (#979)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/docker/cli](https://github.com/docker/cli) | `v29.5.0+incompatible` → `v29.5.1+incompatible` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fdocker%2fcli/v29.5.1+incompatible?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fdocker%2fcli/v29.5.0+incompatible/v29.5.1+incompatible?slim=true) |

---

### Release Notes

<details>
<summary>docker/cli (github.com/docker/cli)</summary>

### [`v29.5.1+incompatible`](https://github.com/docker/cli/compare/v29.5.0...v29.5.1)

[Compare Source](https://github.com/docker/cli/compare/v29.5.0...v29.5.1)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xODIuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE4Mi4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/979
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-19 16:27:04 +00:00
Renovate Bot
9e738c203c fix(deps): update module github.com/go-git/go-git/v5 to v5.19.1 (#980)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) | `v5.19.0` → `v5.19.1` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgo-git%2fgo-git%2fv5/v5.19.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgo-git%2fgo-git%2fv5/v5.19.0/v5.19.1?slim=true) |

---

### Release Notes

<details>
<summary>go-git/go-git (github.com/go-git/go-git/v5)</summary>

### [`v5.19.1`](https://github.com/go-git/go-git/releases/tag/v5.19.1)

[Compare Source](https://github.com/go-git/go-git/compare/v5.19.0...v5.19.1)

#### What's Changed

- v5: plumbing: transport/ssh, Shell-quote path by [@&#8203;hiddeco](https://github.com/hiddeco) in [#&#8203;2068](https://github.com/go-git/go-git/pull/2068)
- v5: git: submodule, Fix relative URL resolution by [@&#8203;hiddeco](https://github.com/hiddeco) in [#&#8203;2070](https://github.com/go-git/go-git/pull/2070)
- v5: git: submodule, canonical remote for relative URLs by [@&#8203;hiddeco](https://github.com/hiddeco) in [#&#8203;2074](https://github.com/go-git/go-git/pull/2074)
- v5: git: submodule, error on remote without URLs by [@&#8203;hiddeco](https://github.com/hiddeco) in [#&#8203;2078](https://github.com/go-git/go-git/pull/2078)
- v5: plumbing: format/idxfile, Validate offset64 indices by [@&#8203;hiddeco](https://github.com/hiddeco) in [#&#8203;2084](https://github.com/go-git/go-git/pull/2084)
- v5: \*: Reject malformed variable-length integers by [@&#8203;hiddeco](https://github.com/hiddeco) in [#&#8203;2092](https://github.com/go-git/go-git/pull/2092)
- v5: plumbing: format/packfile, Tighten delta validation by [@&#8203;hiddeco](https://github.com/hiddeco) in [#&#8203;2091](https://github.com/go-git/go-git/pull/2091)
- v5: Add `worktreeFilesystem` wrapper for worktree and hardening by [@&#8203;hiddeco](https://github.com/hiddeco) in [#&#8203;2100](https://github.com/go-git/go-git/pull/2100)
- v5: config: validate submodule names by [@&#8203;hiddeco](https://github.com/hiddeco) in [#&#8203;2082](https://github.com/go-git/go-git/pull/2082)
- build: Update module github.com/go-git/go-git/v5 to v5.19.0 \[SECURITY] (releases/v5.x) by [@&#8203;go-git-renovate](https://github.com/go-git-renovate)\[bot] in [#&#8203;2111](https://github.com/go-git/go-git/pull/2111)
- v5: git: Allow MkdirAll on worktree-root paths by [@&#8203;hiddeco](https://github.com/hiddeco) in [#&#8203;2117](https://github.com/go-git/go-git/pull/2117)
- v5: git: Stop validating symlink target paths by [@&#8203;pjbgf](https://github.com/pjbgf) in [#&#8203;2116](https://github.com/go-git/go-git/pull/2116)
- v5: plumbing: format decoder input bounds and contracts by [@&#8203;hiddeco](https://github.com/hiddeco) in [#&#8203;2125](https://github.com/go-git/go-git/pull/2125)
- plumbing: format/packfile, cap delta chain depth in parser by [@&#8203;pjbgf](https://github.com/pjbgf) in [#&#8203;2137](https://github.com/go-git/go-git/pull/2137)

**Full Changelog**: <https://github.com/go-git/go-git/compare/v5.19.0...v5.19.1>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xODIuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE4Mi4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/980
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-19 16:18:34 +00:00
Nicolas
6023928876 Fix token use with schemaless Gitea instance (#977)
Fixes #973

## Summary
- Normalize schemaless `--gitea-instance` values before comparing clone URL hosts
- Add regression tests for `GITEA_TOKEN` use with private action/reusable workflow clones on the same instance

---------

Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/977
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-committed-by: Nicolas <bircni@icloud.com>
2026-05-18 02:22:04 +00:00
silverwind
014ce438c1 Add OCI source and version labels to images (#975)
Adds `org.opencontainers.image.source` and `org.opencontainers.image.version` labels to all three image variants (`basic`, `dind`, `dind-rootless`).

- `source` lets tools like renovate retrieve release notes from the source repo.
- `version` exposes the build version on the image itself.

Both `release-tag` and `release-nightly` workflows pass `VERSION` as a build arg so the label reflects the actual git tag (or `git describe` output for nightly).

---
This PR was written with the help of Claude Opus 4.7

---------

Reviewed-on: https://gitea.com/gitea/runner/pulls/975
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-17 18:15:19 +00:00
Jacob Alberty
cf7e29c10d fix(parse_env_file): support env-file lines larger than 64 KiB (#974)
My builds kept flaking out with errors like `invalid format delimiter 'ghadelimiter_...' not found before end of file` or just strange failures in the complete job. After some digging I found an issue in `parseEnvFile` and have tested this fix against the test case presented.

  - `parseEnvFile` reads `$GITHUB_ENV` / `$GITHUB_OUTPUT` with a `bufio.Scanner` using the default 64 KiB token size, and never checks `s.Err()`.
  - Any action that writes a multi-line value with a single line >64 KiB silently aborts the scan with `bufio.ErrTooLong`, which surfaces as the misleading `"invalid format delimiter
  'ghadelimiter_…' not found before end of file"`.
  - Real-world trigger: `docker/build-push-action`'s `metadata` output embeds the full `GITHUB_EVENT_PATH` payload via buildx provenance; a long PR description (e.g. a Renovate dependency
  table) puts the body field on one JSON-escaped line well past 64 KiB.
  - Raise the scanner buffer to 1 MiB so realistic outputs parse.

### Reproduction
Test this in an action. This removes the `docker/build-push-action` aspect and reproduces it directly.
```yaml
  jobs:
    repro:
      runs-on: ubuntu-latest
      steps:
        - id: big
          run: |
            {
              echo 'value<<EOF'
              head -c 70000 /dev/urandom | base64 -w0
              echo
              echo 'EOF'
            } >> "$GITHUB_OUTPUT"
```

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/974
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Jacob Alberty <jacob.alberty@gmail.com>
Co-committed-by: Jacob Alberty <jacob.alberty@gmail.com>
2026-05-17 13:00:17 +00:00
Nicolas
8a99506fed Fix host cleanup, volume allowlist, cache upload, and action host edge cases (#970)
## Summary
- prevent host-mode execution from deleting caller-owned workdirs
- harden `valid_volumes` checks against `..` and symlink escapes
- return immediately after artifact cache upload write failures
- default implicit remote action clone hosts to `GitHubInstance`/`github.com`

Authored with assistance from OpenAI Codex GPT-5.

---------

Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/970
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
2026-05-17 12:53:04 +00:00
silverwind
5873b8b054 Remove dead code from act/ (#971)
Removes code that whole-program reachability analysis (`deadcode` from `golang.org/x/tools`) confirmed unreachable, plus the `act/workflowpattern` package which no file outside its own directory imports.

- `act/common/draw.go` — CLI box-drawing helpers left over from nektos/act's dropped CLI
- `act/common/file.go` — `CopyFile`/`CopyDir` package-level helpers (container types have their own `CopyDir` methods, kept)
- `act/common/executor.go` — `Warning` type and `Warningf`. The `case Warning:` arm in `(Executor).Then`'s type switch was dead too (no code ever constructed a `Warning`); the switch is replaced with `if err != nil { return err }`
- `act/lookpath/env.go` — `LookPath` no-arg wrapper and `defaultEnv` struct. Only `LookPath2(file, env)` was used externally; the `Env` interface is kept
- `act/runner/action_cache_offline_mode.go` — `GoGitActionCacheOfflineMode` wrapper, never instantiated
- `act/workflowpattern/` — entire package, never imported

Net `-943` lines.

---
This PR was written with the help of Claude Opus 4.7

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/971
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-17 03:53:28 +00:00
Lunny Xiao
5464d33eef fix: Return if executors length is zero in ParallelExecutor (#960)
It displayed an unused log and start an unused go routine. We should check the executors number before continue.

```
INFO[2026-05-12T21:01:04-07:00] Running job with maxParallel=1 for 1 matrix combinations
INFO[2026-05-12T21:01:04-07:00] NewParallelExecutor: Creating 1 workers for 1 executors
INFO[2026-05-12T21:01:04-07:00] NewParallelExecutor: Creating 1 workers for 0 executors
INFO[2026-05-12T21:01:04-07:00] NewParallelExecutor: Creating 1 workers for 0 executors
```

Reviewed-on: https://gitea.com/gitea/runner/pulls/960
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-05-15 22:14:13 +00:00
silverwind
3c5f03ff8f feat: make pseudo-TTY allocation opt-in (#961)
Fixes #956.

Pseudo-TTY allocation is now an explicit, runner-wide opt-in via `runner.allocate_pty`, applied to both host and docker backends. Default is off, matching GitHub `actions/runner`.

```yaml
runner:
  allocate_pty: false  # default
```

**Before:** the host backend hardcoded `if true /* allocate Terminal */` and the docker backend used `term.IsTerminal(os.Stdout.Fd())`. As a result, `docker build` (and other TTY-aware tools) saw a TTY and emitted cursor-control redraw frames that flooded captured logs with thousands of duplicate-looking progress lines — only on host-mode runners in production, and on docker-mode runners when the daemon happened to be launched from a shell rather than a service.

**After:** both backends consult `Config.AllocatePTY`. The `term.IsTerminal` heuristic is gone, so behavior no longer depends on whether the daemon has a controlling terminal.

**Reproduction:** running `docker build` through `HostEnvironment.Exec` with output captured to a buffer:

| | Before (`if true`) | After (`AllocatePTY=false`) |
|---|---:|---:|
| bytes captured | 18,167 | 1,048 |
| ANSI CSI sequences | 556 | 0 |
| cursor-up `\e[1A` | 181 | 0 |

**Side fix:** `ptyWriter.AutoStop` is now `atomic.Bool`. The field is written from the exec goroutine after `cmd.Wait()` and read from the `copyPtyOutput` goroutine via `ptyWriter.Write`; existing tests never tripped the race detector because their commands produced no output before exit. The new host-mode test does.

---
This PR was written with the help of Claude Opus 4.7

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/961
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-committed-by: silverwind <2021+silverwind@noreply.gitea.com>
2026-05-15 18:11:39 +00:00
Renovate Bot
880e9755d9 chore(deps): update workflow dependencies (major) (#968)
Reviewed-on: https://gitea.com/gitea/runner/pulls/968
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-15 14:08:07 +00:00
Renovate Bot
8d7cf48a6f fix(deps): update module github.com/docker/cli to v29.5.0+incompatible (#969)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/docker/cli](https://github.com/docker/cli) | `v29.4.3+incompatible` → `v29.5.0+incompatible` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fdocker%2fcli/v29.5.0+incompatible?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fdocker%2fcli/v29.4.3+incompatible/v29.5.0+incompatible?slim=true) |

---

### Release Notes

<details>
<summary>docker/cli (github.com/docker/cli)</summary>

### [`v29.5.0+incompatible`](https://github.com/docker/cli/compare/v29.4.3...v29.5.0)

[Compare Source](https://github.com/docker/cli/compare/v29.4.3...v29.5.0)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNzkuNCIsInVwZGF0ZWRJblZlciI6IjQzLjE3OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/969
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-15 14:05:52 +00:00
Renovate Bot
f23605c614 chore(deps): update workflow dependencies (major) (#967)
Reviewed-on: https://gitea.com/gitea/runner/pulls/967
Reviewed-by: techknowlogick <9+techknowlogick@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-14 16:54:48 +00:00
thisisqasim
00b7fec80f Simplify kubernetes dind example allowing for default docker config in workflows (#709)
With this docker clients in workflows can connect on the default socket without needing to change DOCKER_HOST. Startup probe also removes the need for custom shell command.

Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/709
Co-authored-by: thisisqasim <40013+thisisqasim@noreply.gitea.com>
Co-committed-by: thisisqasim <40013+thisisqasim@noreply.gitea.com>
2026-05-14 05:52:41 +00:00
silverwind
dda5841af8 chore(deps): bump retry-go, golangci-lint, govulncheck (#965)
Bumps `github.com/avast/retry-go` v4.7.0 -> v5.0.0, `golangci-lint` v2.11.4 -> v2.12.2 (aligns with gitea/gitea), and pins `govulncheck` to v1.3.0.

- `retry-go` v5 replaces the package-level `retry.Do(fn, opts...)` with a builder API `retry.New(opts...).Do(fn)`. The single call site in `internal/pkg/report/reporter.go` was migrated.
- `golangci-lint` v2.12.2 surfaces three new findings in `act/` (modernize/slicesbackward, govet/inline): one backward loop now uses `slices.Backward`, and the deprecated `reflect.Ptr` alias is replaced with `reflect.Pointer`.
- `go.mod`: the two direct-`require` blocks are merged into one, and a stray `gopkg.in/yaml.v3 // indirect` is moved into the indirect block. Purely cosmetic; `go.sum` is unchanged.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/965
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-14 05:39:28 +00:00
silverwind
32bed52686 fix(deps): bump docker deps, switch to moby/moby (#943)
Fixes: https://gitea.com/gitea/runner/issues/859

Migration approach mirrors [actions-oss/act-cli#154](https://github.com/actions-oss/act-cli/pull/154).

### Dependency changes

- `github.com/docker/docker` v25.0.15 → **removed** (v29 doesn't exist as docker/docker; the project moved to moby/moby)
- `github.com/docker/cli` v25.0.7 → v29.4.3
- `github.com/docker/go-connections` v0.6.0 → v0.7.0
- `github.com/docker/docker-credential-helpers` v0.9.5 → v0.9.6
- `github.com/moby/go-archive` added at v0.2.0
- `github.com/moby/moby/api` added at v1.54.2
- `github.com/moby/moby/client` added at v0.4.1
- `github.com/moby/buildkit` removed (only used `dockerignore.ReadAll`, swapped for `moby/patternmatcher/ignorefile.ReadAll` directly)
- `github.com/containerd/errdefs` v0.3.0 → v1.0.0

### Migration

- v28: type aliases moved to their subpackages (`types.{Container,Image,Network,Exec}*` → `container/image/network/...`); deprecated APIs replaced (`ImageInspectWithRaw`, `client.IsErrNotFound`, `archive.CanonicalTarNameForPath`, `opts.ValidateMACAddress`, `ListOpts.GetAll`)
- v29: structural client redesign — every `cli.X(ctx, ...)` call switched to options-everywhere/Result-typed signatures, `ContainerExec*` → `Exec*`, `ContainerWait` returns a struct with `Result`/`Error` channels, `Tty`→`TTY`, `Copy*Container` takes options struct, `client.NewClientWithOpts` → `client.New`. `pkg/stdcopy` moved to `moby/moby/api/pkg/stdcopy`. The vendored copy of `cli/command/container/opts.go` was refreshed from cli v29 (now uses `netip.Addr` for IPs, port-set conversion helpers). A small local `parsePlatform` helper centralises the `os/arch[/variant]` parsing previously inlined into multiple call sites.

### Behaviour preservation

The migration introduced several behavioural shifts vs the v25 client; all were caught in review and reverted/fixed in follow-up commits:

- `GetDockerClient`: cli v29's `Ping(NegotiateAPIVersion: true)` returns errors that the old `NegotiateAPIVersion` silently swallowed. Restored best-effort behaviour (warn-log + continue) so daemons with blocked `_ping` or API < 1.40 keep working. The SSH-helper `client.New` call no longer inherits `client.FromEnv`, matching the old `NewClientWithOpts(WithHost, WithDialContext)` so `DOCKER_API_VERSION`/`DOCKER_TLS_VERIFY` don't leak into the SSH-tunneled client
- `parsePlatform`: malformed input now returns an explicit error instead of silently dropping to "no platform constraint" and pulling the host-default architecture. Single-segment (`"linux"`), 4+-segment (`"linux/arm/v7/extra"`), and trailing-slash (`"linux/arm/"`) inputs are all rejected
- `LoadDockerAuthConfig`/`LoadDockerAuthConfigs`: `config.LoadDefaultConfigFile(nil)` panics on a malformed config file (it does `fmt.Fprintln` on the nil `io.Writer`). Switched to `config.Load(config.Dir())` so load errors reach the logger and the panic path is gone. Restored the old behaviour of returning `config.Load` and `GetAuthConfig` errors to the caller (the v29 refactor had silently downgraded them to warn-only). A `reference.ParseNormalizedNamed` failure on the image string falls through to the `docker.io` default rather than aborting, since the old string-based hostname extraction was infallible

Test assertions also updated for two upstream error-message string shifts (`go-connections` port-range parser; `cli/opts` envfile BOM check). Added unit-test coverage for the new `parsePlatform` helper, locking in the intentional limits (single-segment, 4+-segment, and trailing-slash platforms rejected).

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/943
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-14 02:29:05 +00:00
Lunny Xiao
a7e972d8de fix: respect proxy env vars in runner client (#962)
Fixes #957.

## Why
The runner builds a custom `http.Transport` for its RPC client. From first principles, once we stop using the default transport we also stop inheriting its default proxy resolution behavior, so `HTTP_PROXY`/`HTTPS_PROXY` are ignored unless we wire that behavior back explicitly.

## What
- set `Proxy: http.ProxyFromEnvironment` on the custom transport
- add a regression test that verifies `getHTTPClient` honors proxy environment variables

Reviewed-on: https://gitea.com/gitea/runner/pulls/962
Reviewed-by: ChristopherHX <38043+christopherhx@noreply.gitea.com>
2026-05-13 19:10:52 +00:00
Lunny Xiao
763b38ece3 fix: isolate per-task runner envs (#959)
## Summary
- clone the runner environment map for each task before injecting runtime and OIDC tokens
- keep the shared base environment immutable so concurrent jobs cannot hit `concurrent map writes`
- add a unit test covering task-local env cloning

Fixes #958

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/959
Reviewed-by: Nicolas <bircni@icloud.com>
2026-05-12 18:50:25 +00:00
Renovate Bot
a1f13cb970 fix(deps): update module github.com/opencontainers/selinux to v1.14.1 (#955)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/opencontainers/selinux](https://github.com/opencontainers/selinux) | `v1.14.0` → `v1.14.1` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fopencontainers%2fselinux/v1.14.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fopencontainers%2fselinux/v1.14.0/v1.14.1?slim=true) |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/856) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNzAuMjAiLCJ1cGRhdGVkSW5WZXIiOiI0My4xNzAuMjAiLCJ0YXJnZXRCcmFuY2giOiJtYWluIiwibGFiZWxzIjpbXX0=-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/955
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-12 13:34:52 +00:00
silverwind
1e3ab0c40a fix(deps): update mergo to v1.0.2 (now dario.cat/mergo) (#954)
At v1.0.0 the `github.com/imdario/mergo` module was relocated to `dario.cat/mergo`, so a plain version bump (as in https://gitea.com/gitea/runner/pulls/951) leaves the import path pointing at the old, unmaintained location. This PR updates the import in `act/container/docker_run.go` and adjusts `go.mod` accordingly. The public API (`mergo.Merge`, `mergo.WithOverride`, `mergo.WithAppendSlice`) is unchanged.

Supersedes https://gitea.com/gitea/runner/pulls/951.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/954
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-10 22:05:05 +00:00
silverwind
295eecb9af fix: ensure dbfs_data is cleaned up after task completion (#952)
Fixes #950.

After #819, the daemon flushes logs eagerly on the job-result entry (via the `stateNotify` path), so `Close()` typically runs `ReportLog(true)` with an empty buffer. Gitea's `UpdateLog` handler short-circuits on `len(Rows)==0` before honoring `NoMore`, so the final request never runs `TransferLogs` and `dbfs_data` rows leak. The server-side short-circuit is latent since the original Actions implementation in 2023; #819 made it deterministically reachable.

Workaround: inject a sentinel row in `Close()` after the daemon has exited so the final `UpdateLog` always carries at least one row. Done after the daemon waits so the sentinel can't be flushed before `ReportLog(true)` reads it.

https://github.com/go-gitea/gitea/pull/37631 drops the empty-rows short-circuit when `NoMore=true`; that would work with or without this PR.

Reviewed-on: https://gitea.com/gitea/runner/pulls/952
Reviewed-by: Nicolas <bircni@icloud.com>
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-10 20:55:57 +00:00
Zettat123
ef6ca957b5 fix(artifactcache): preserve cache key case to stop redundant uploads (#947)
## Summary

`artifactcache.Handler` was lowercasing cache keys before storing and returning them. This caused actions like `actions/setup-go` to treat every restore as a partial hit and re-upload the cache on every job run.

Similar issue: [act#2497](https://github.com/nektos/act/issues/2497)

## Root Cause

These actions build cache keys that include `RUNNER_OS` (e.g. `setup-go-Linux-x64-...` See [setup-go/cache-restore.ts](78961f6f84/src/cache-restore.ts (L11-L51)) ). In `gitea/runner`,  `RUNNER_OS` is always `Linux` by default (See https://gitea.com/gitea/runner/search?q=RUNNER_OS).

These actions decide whether to save the cache data in their post step using **strict** `===` comparison between the primary key and the key returned from the runner. See [setup-go cache-save.ts](78961f6f84/src/cache-save.ts (L44-L86)) .

|State | Value|
|--- | ---|
|CachePrimaryKey | `setup-go-Linux-x64-ubuntu-22.04-go-1.24.9-abc123` |
|CacheMatchedKey | `setup-go-linux-x64-ubuntu-22.04-go-1.24.9-abc123` |

Because the runner's cache server lowercased the stored key, the response carried `setup-go-linux-...` while the action's primary key was `setup-go-Linux-...`. Strict equality failed, then the actions updated same data again. This repeated on every run, wasting disk and bandwidth. The duplicate blobs accumulate until GC .

https://gitea.com/gitea/runner/actions/runs/462560/jobs/737401#jobstep-2-15
![image.png](/attachments/d3487457-1d09-44b5-9937-a0b8cab1bcc5)

https://gitea.com/gitea/runner/actions/runs/462560/jobs/737401#jobstep-6-22
![image.png](/attachments/9217dc71-cb0c-456b-a516-0017458123c7)

## Fix
Drop the `strings.ToLower` calls in `find` and `reserve` so the original key case is preserved end-to-end. This fix will invalidate existing "case insensitive" keys.

## Notes

The [original act review](https://github.com/nektos/act/pull/1770/changes/BASE..d44b8d15649d9d09d1d891130b8f3962097a81f3#r1177624608) suggested making cache keys case-insensitive because `isExactKeyMatch` compares cache key ignoring case. However, the actions (`setup-go` / `setup-node` / `setup-ruby`) compare with strict `===` rather than `isExactKeyMatch`.

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/947
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2026-05-09 12:27:52 +00:00
Renovate Bot
8088df52b9 fix(deps): update module golang.org/x/term to v0.43.0 (#948)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) | [`v0.42.0` → `v0.43.0`](https://cs.opensource.google/go/x/term/+/refs/tags/v0.42.0...refs/tags/v0.43.0) | ![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2fterm/v0.43.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2fterm/v0.42.0/v0.43.0?slim=true) |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/856) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjkuNCIsInVwZGF0ZWRJblZlciI6IjQzLjE2OS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/948
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-09 05:28:44 +00:00
Nicolas
3ea7d39690 fix: overwrite read-only files when copying action directories (#942)
## Summary

  - `CopyCollector.WriteFile` now removes any existing destination file
    before writing, handling read-only modes (e.g. git pack files at
    `0444`) that cause `EACCES`/`ERROR_ACCESS_DENIED` on macOS and Windows.
  - Added `O_TRUNC` to the `OpenFile` flags as a safety net.

  ## Root cause

  When a composite action with a post step runs on a host runner,
  `runPostStep` calls `maybeCopyToActionDir`, which re-copies the action
  into `miscpath/act/actions/<name>/`. The first copy (main step) writes
  `.git/objects/pack/*.idx` at the destination with mode `0444` (as set
  by go-git). The second copy (post step) calls
  `os.OpenFile(dest, O_CREATE|O_WRONLY, …)` on that existing `0444` file,
  which fails immediately:

  - macOS: `open <path>: permission denied`
  - Windows: `open <path>: Access is denied`

Fixes: https://gitea.com/gitea/runner/issues/941
Fixes: https://gitea.com/gitea/runner/issues/876

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/942
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-committed-by: Nicolas <bircni@icloud.com>
2026-05-08 04:11:42 +00:00
Schallbert
861d351845 add apparmor=rootlesskit in security_opt (#937)
paste depends_on chain from socket-runner-setup  to runner-dind-setup
add apparmor=rootlesscit in security_opt
add explanations for elevated privileges

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/937
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Schallbert <schallbert@mailbox.org>
Co-committed-by: Schallbert <schallbert@mailbox.org>
2026-05-07 21:20:33 +00:00
silverwind
cce8543d06 fix: serialize action-cache reads to prevent worktree race (#938)
`NewGitCloneExecutor` holds a per-directory mutex while it `git checkout --force`s a remote action into the shared `<ActionCacheDir>/<UsesHash>`, but four read sites ran unlocked:

- `maybeCopyToActionDir`'s tar walk via `JobContainer.CopyDir`
- `prepareActionExecutor`'s `readAction` parse of `action.yml`
- `newReusableWorkflowExecutor`'s `model.NewWorkflowPlanner` after `cloneRemoteReusableWorkflow` released its lock
- `execAsDocker` when `ActionCache == nil`: `docker build` walks `contextDir` for the daemon-side build context

When two matrix jobs share a `uses:`, a read interleaved with a peer's checkout produces partial state — observed as `Cannot find module .../dist/index.js` and `setup-uv` failing on a half-written `action.yml`.

Exports `acquireCloneLock` as `AcquireCloneLock` and takes it at all four sites. `container.ImageExistsLocally` / `NewDockerBuildExecutor` and `model.NewWorkflowPlanner` are indirected through package-level vars so the docker-action build path and the reusable-workflow read site are testable without a real daemon, mirroring `ContainerNewContainer`. Three regression tests cover the higher-risk sites (`maybeCopyToActionDir`, `execAsDocker`, `newReusableWorkflowExecutor`); each fails if its `AcquireCloneLock` is removed.

Subsumed by https://gitea.com/gitea/runner/pulls/814 once that lands. Related: https://gitea.com/gitea/runner/pulls/930

---
This PR was written with the help of Claude Opus 4.7

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/938
Reviewed-by: Nicolas <bircni@icloud.com>
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-07 19:57:04 +00:00
silverwind
75643645f0 feat: remove emojis from runner logging, add Starting job container group (#940)
Aligns runner log output more closely with `actions/runner`:

- Strip the whale, rocket, cloud, construction, chequered-flag, and exclamation-mark glyphs from log lines and drop the now-unused `logPrefix` constant.
- Reword `no outputs used step '%s'` → `No outputs registered for step '%s'` (the original was ungrammatical and inaccurate — it fires when `set-output` references an unknown step ID).
- Wrap the docker pull/network/create/start phase of job container startup in a `::group::Starting job container` / `::endgroup::` collapsible section, mirroring `actions/runner`. Since act drives Docker through the SDK rather than the CLI, we can't echo `##[command]/usr/bin/docker create ...` lines verbatim — instead the helper emits a summary inside the group:

  ```
  ::group::Starting job container
  image: <image>
  name: <container-name>
  network: <network-name>
  ::endgroup::
  ```

- Extracted the emit into a `printStartJobContainerGroup` helper (parallel to `printRunActionHeader` in `step_run.go`) and added a golden-style test `TestPrintStartJobContainerGroupGolden`.
- Drive-by: replace two remaining literal `"raw_output"` strings in `run_context.go` with the existing `rawOutputField` constant.

Closes #935

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/940
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-07 19:32:53 +00:00
Renovate Bot
dff63b3ecc fix(deps): update module github.com/go-git/go-git/v5 to v5.19.0 (#934)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) | `v5.18.0` → `v5.19.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fgo-git%2fgo-git%2fv5/v5.19.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fgo-git%2fgo-git%2fv5/v5.18.0/v5.19.0?slim=true) |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/856) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjUuMSIsInVwZGF0ZWRJblZlciI6IjQzLjE2NS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/934
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-07 01:27:20 +00:00
Renovate Bot
a5d9fe9651 fix(deps): update module github.com/opencontainers/selinux to v1.14.0 (#928)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/opencontainers/selinux](https://github.com/opencontainers/selinux) | `v1.13.1` → `v1.14.0` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fopencontainers%2fselinux/v1.14.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fopencontainers%2fselinux/v1.13.1/v1.14.0?slim=true) |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/856) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjMuMyIsInVwZGF0ZWRJblZlciI6IjQzLjE2My4zIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/928
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-07 01:27:06 +00:00
silverwind
d607f3b342 test: clean up dead/stale fixtures and bump test container images (#932)
Audit-driven cleanup of `act/` test fixtures. Three commits:

**1. Remove dead fixtures** — 12 fixture directories that no Go test references: `dir with spaces`, `environment-variables`, `issue-104`, `issue-122`, `issue-141`, `localdockerimagetest_`, `node`, `parallel`, `python`, `uses-composite-with-inputs`, `uses-composite-with-pre-and-post-steps`, `shells/custom` (under `act/runner/testdata/`), plus `act/artifactcache/testdata/example`.

**2. Collapse `actions/node{12,16,20}` to a single `actions/node24` fixture** — the trio dispatched through identical `IsNode()` code paths and exercised the container's node binary, not the `using:` string. Bumps bundled deps to current (`@actions/core@^3`, `@actions/github@^9`, `@vercel/ncc@^0.38.4`) — both runtime packages are now ESM-only, so `index.js` is rewritten to ESM and `"type": "module"` added. Drops committed `node_modules/` and `package-lock.json` (now gitignored locally; `dist/` continues to be ignored by the repo-root `.gitignore` as before). Reduces `local-action-js/push.yml` to a single `test-node24` job and bumps four other stale `using: node12/16` references in fixtures.

**3. Bump test container base images** to `node:24-bookworm-slim` / `node:24-bookworm` / `ubuntu:24.04`. Replaces `node:16-buster-slim`, `node:16-buster`, `node:12.20.1-buster-slim`, and the EOL `node:12-buster-slim` / `node:16-buster-slim` / `ubuntu:18.04` base images in `actions/{docker-local,docker-local-noargs,action1}/Dockerfile`.

The runner's model still accepts `using: node12/16/20` for third-party actions in the wild — those constants are untouched.

Fixes: https://gitea.com/gitea/runner/issues/931

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/932
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-07 00:11:56 +00:00
silverwind
5e59402fb2 fix: re-fetch cached reusable workflow on every run (#930)
`cloneIfRequired` only ran the underlying clone executor when the target directory was missing, so a reusable workflow referenced by a moving ref (`uses: org/repo/.gitea/workflows/wf.yml@master`) was cached forever after the first invocation — edits to the source file never propagated.

Always invoke `git.NewGitCloneExecutor`. It handles existing repositories via fetch + pull + hard-reset, so branch and tag refs are brought up to date on each run, matching GitHub Actions semantics.

Drops the global `executorLock` too: `NewGitCloneExecutor` already takes a per-directory lock via `acquireCloneLock`, so the outer mutex only added unnecessary serialization across unrelated reusable-workflow clones — worse now that every invocation runs the full fetch.

Includes a regression test that drives the wrapper against a local bare repo, pushes a new commit on `master` between two invocations, and asserts the cached workflow file reflects the new tip.

Fixes: https://github.com/go-gitea/gitea/issues/37483
Fixes: https://gitea.com/gitea/runner/issues/726
Related: https://github.com/go-gitea/gitea/issues/30543

Would be subsumed by https://gitea.com/gitea/runner/pulls/814 ("WIP: Introduce new action cache") once that lands.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/930
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-06 16:10:27 +00:00
Renovate Bot
dfeb463904 chore(deps): update docker docker tag to v29 (#924)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| docker | stage | major | `28-dind-rootless` → `29-dind-rootless` |
| docker | stage | major | `28-dind` → `29-dind` |

---

> ⚠️ **Warning**
>
> Some dependencies could not be looked up. Check the [Dependency Dashboard](issues/856) for more information.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about these updates again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuNiIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC42IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/924
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-05 23:14:36 +00:00
silverwind
594c9ade7c Align step failure log output with GitHub Actions (#927)
Fixes #926.

Before:

<img src="/attachments/a5ae9221-eee2-410a-964e-6103ce126df4" alt="image.png" width="400">

After:

<img width="400" alt="image.png" src="attachments/2f2d67c4-6080-4ec3-9ae5-df33e6479920">

Also gets rid of a bunch of emojis in the logging and the obsolete link to `nektos/act` and align some other error messages.

---
This PR was written with the help of Claude Opus 4.7

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/927
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-05-05 20:17:32 +00:00
Nicolas
2a4d56c650 feat: add startup janitor for stale bind-workdir task workspaces (#870)
- Add idle-time cleanup for stale bind-workdir task directories instead of cleaning them on the task execution path.
- Make cleanup behavior configurable with `runner.startup_cleanup_age` as the stale-age threshold (default: `24h`) and `runner.idle_cleanup_interval` as the idle cleanup cadence (default: `10m`).
- Restrict cleanup scope to numeric task directory names only, to avoid touching operator-managed folders.
- Document the cleanup settings in `config.example.yaml` and `README.md`.
- Add tests for stale-directory cleanup, idle cleanup throttling, and config default/override parsing.

## Why

When a runner or host crashes, normal per-task cleanup may not run, leaving stale task directories under the bind-workdir root. Running this cleanup only while the runner is idle recovers that disk space without adding overhead to active job execution.

If you want, I can also tighten the wording around `startup_cleanup_age`, since the key name now reads a bit misleadingly relative to the actual behavior.

---------

Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/870
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
2026-05-05 20:11:44 +00:00
Nicolas
a22119cf88 fix(host): correct host workspace cleanup on Windows (#883)
## Summary
- Fix host-mode cleanup to remove the job **workspace** directory after a run (instead of leaving checkouts behind).
- On Windows, track step process PIDs and terminate remaining process trees during teardown before attempting workspace deletion (prevents file-lock failures).
- Skip workspace deletion when `bind_workdir` is enabled to avoid conflicting with runner-level task directory cleanup.

## Implementation details
- `HostEnvironment` now records PIDs for started commands and best-effort terminates them on Windows during `Remove()`.
- Workspace removal uses a small retry loop on Windows to handle transient locks.
- `BindWorkdir` is propagated into `HostEnvironment` so cleanup behavior matches runner configuration.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/883
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
2026-05-05 18:28:12 +00:00
Renovate Bot
b68ecf2580 chore(deps): update crazy-max/ghaction-import-gpg action to v7 (#923)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [crazy-max/ghaction-import-gpg](https://github.com/crazy-max/ghaction-import-gpg) | action | major | `v6` → `v7` |

---

### Release Notes

<details>
<summary>crazy-max/ghaction-import-gpg (crazy-max/ghaction-import-gpg)</summary>

### [`v7`](https://github.com/crazy-max/ghaction-import-gpg/compare/v7.0.0...v7.0.0)

[Compare Source](https://github.com/crazy-max/ghaction-import-gpg/compare/v7.0.0...v7.0.0)

### [`v7.0.0`](https://github.com/crazy-max/ghaction-import-gpg/releases/tag/v7.0.0)

[Compare Source](https://github.com/crazy-max/ghaction-import-gpg/compare/v6.3.0...v7.0.0)

- Node 24 as default runtime (requires [Actions Runner v2.327.1](https://github.com/actions/runner/releases/tag/v2.327.1) or later) by [@&#8203;crazy-max](https://github.com/crazy-max) in [#&#8203;241](https://github.com/crazy-max/ghaction-import-gpg/pull/241)
- Switch to ESM and update config/test wiring by [@&#8203;crazy-max](https://github.com/crazy-max) in [#&#8203;239](https://github.com/crazy-max/ghaction-import-gpg/pull/239)
- Bump [@&#8203;actions/core](https://github.com/actions/core) from 1.11.1 to 3.0.0 in [#&#8203;232](https://github.com/crazy-max/ghaction-import-gpg/pull/232)
- Bump [@&#8203;actions/exec](https://github.com/actions/exec) from 1.1.1 to 3.0.0 in [#&#8203;242](https://github.com/crazy-max/ghaction-import-gpg/pull/242)
- Bump brace-expansion from 1.1.11 to 1.1.12 in [#&#8203;221](https://github.com/crazy-max/ghaction-import-gpg/pull/221)
- Bump minimatch from 3.1.2 to 3.1.5 in [#&#8203;240](https://github.com/crazy-max/ghaction-import-gpg/pull/240)
- Bump openpgp from 6.1.0 to 6.3.0 in [#&#8203;233](https://github.com/crazy-max/ghaction-import-gpg/pull/233)

**Full Changelog**: <https://github.com/crazy-max/ghaction-import-gpg/compare/v6.3.0...v7.0.0>

### [`v6.3.0`](https://github.com/crazy-max/ghaction-import-gpg/releases/tag/v6.3.0)

[Compare Source](https://github.com/crazy-max/ghaction-import-gpg/compare/v6.2.0...v6.3.0)

- Bump openpgp from 5.11.2 to 6.1.0 in [#&#8203;215](https://github.com/crazy-max/ghaction-import-gpg/pull/215)
- Bump cross-spawn from 7.0.3 to 7.0.6 in [#&#8203;212](https://github.com/crazy-max/ghaction-import-gpg/pull/212)

**Full Changelog**: <https://github.com/crazy-max/ghaction-import-gpg/compare/v6.2.0...v6.3.0>

### [`v6.2.0`](https://github.com/crazy-max/ghaction-import-gpg/releases/tag/v6.2.0)

[Compare Source](https://github.com/crazy-max/ghaction-import-gpg/compare/v6.1.0...v6.2.0)

- Bump [@&#8203;actions/core](https://github.com/actions/core) from 1.10.1 to 1.11.1 in [#&#8203;209](https://github.com/crazy-max/ghaction-import-gpg/pull/209)
- Bump braces from 3.0.2 to 3.0.3 in [#&#8203;203](https://github.com/crazy-max/ghaction-import-gpg/pull/203)
- Bump ip from 2.0.0 to 2.0.1 in [#&#8203;196](https://github.com/crazy-max/ghaction-import-gpg/pull/196)
- Bump micromatch from 4.0.4 to 4.0.8 in [#&#8203;207](https://github.com/crazy-max/ghaction-import-gpg/pull/207)
- Bump openpgp from 5.11.0 to 5.11.2 in [#&#8203;205](https://github.com/crazy-max/ghaction-import-gpg/pull/205)
- Bump tar from 6.1.14 to 6.2.1 in [#&#8203;198](https://github.com/crazy-max/ghaction-import-gpg/pull/198)

**Full Changelog**: <https://github.com/crazy-max/ghaction-import-gpg/compare/v6.1.0...v6.2.0>

### [`v6.1.0`](https://github.com/crazy-max/ghaction-import-gpg/releases/tag/v6.1.0)

[Compare Source](https://github.com/crazy-max/ghaction-import-gpg/compare/v6...v6.1.0)

- Bump [@&#8203;actions/core](https://github.com/actions/core) from 1.10.0 to 1.10.1 in [#&#8203;186](https://github.com/crazy-max/ghaction-import-gpg/pull/186)
- Bump [@&#8203;babel/traverse](https://github.com/babel/traverse) from 7.17.3 to 7.23.2 in [#&#8203;191](https://github.com/crazy-max/ghaction-import-gpg/pull/191)
- Bump debug from 4.1.1 to 4.3.4 in [#&#8203;190](https://github.com/crazy-max/ghaction-import-gpg/pull/190)
- Bump openpgp from 5.10.1 to 5.11.0 in [#&#8203;192](https://github.com/crazy-max/ghaction-import-gpg/pull/192)

**Full Changelog**: <https://github.com/crazy-max/ghaction-import-gpg/compare/v6.0.0...v6.1.0>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuNiIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC42IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/923
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-04 12:44:49 +00:00
Renovate Bot
d1434237c2 fix(deps): update module golang.org/x/term to v0.42.0 (#920)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [golang.org/x/term](https://pkg.go.dev/golang.org/x/term) | [`v0.41.0` → `v0.42.0`](https://cs.opensource.google/go/x/term/+/refs/tags/v0.41.0...refs/tags/v0.42.0) | ![age](https://developer.mend.io/api/mc/badges/age/go/golang.org%2fx%2fterm/v0.42.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/golang.org%2fx%2fterm/v0.41.0/v0.42.0?slim=true) |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [x] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuNCIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/920
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-03 11:36:03 +00:00
Renovate Bot
35c65e2b14 chore(deps): update actions/hello-world-docker-action action to v2 (#921)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [actions/hello-world-docker-action](https://github.com/actions/hello-world-docker-action) | action | major | `v1` → `v2` |

---

### Release Notes

<details>
<summary>actions/hello-world-docker-action (actions/hello-world-docker-action)</summary>

### [`v2`](https://github.com/actions/hello-world-docker-action/releases/tag/v2): Version v2

[Compare Source](https://github.com/actions/hello-world-docker-action/compare/v1...v2)

Update action to use the new environment file method for setting outputs.

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNjAuNCIsInVwZGF0ZWRJblZlciI6IjQzLjE2MC40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/921
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-03 04:44:33 +00:00
Nicolas
c45a4e6d32 ci: Fix triggers (#882)
Currently on a branch a workflow got triggered 2x one time on push and one time as its a PR
Now it only gets triggered on PR and on push onto main

Reviewed-on: https://gitea.com/gitea/runner/pulls/882
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-committed-by: Nicolas <bircni@icloud.com>
2026-05-01 16:37:37 +00:00
Renovate Bot
68d9fc45c9 chore(deps): update dependency @vercel/ncc to ^0.38.0 (#881)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@vercel/ncc](https://github.com/vercel/ncc) | [`^0.24.1` → `^0.38.0`](https://renovatebot.com/diffs/npm/@vercel%2fncc/0.24.1/0.38.4) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@vercel%2fncc/0.38.4?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@vercel%2fncc/0.24.1/0.38.4?slim=true) |

---

### Release Notes

<details>
<summary>vercel/ncc (@&#8203;vercel/ncc)</summary>

### [`v0.38.4`](https://github.com/vercel/ncc/releases/tag/0.38.4)

[Compare Source](https://github.com/vercel/ncc/compare/0.38.3...0.38.4)

##### Bug Fixes

- **cjs-build:** enable evaluating import.meta in cjs build ([#&#8203;1236](https://github.com/vercel/ncc/issues/1236)) ([e72d34d](e72d34d97e)), closes [/github.com/vercel/ncc/pull/897#discussion\_r836916315](https://github.com//github.com/vercel/ncc/pull/897/issues/discussion_r836916315) [#&#8203;1019](https://github.com/vercel/ncc/issues/1019)

### [`v0.38.3`](https://github.com/vercel/ncc/releases/tag/0.38.3)

[Compare Source](https://github.com/vercel/ncc/compare/0.38.2...0.38.3)

##### Bug Fixes

- add missing `--asset-builds` to cli help message ([#&#8203;1228](https://github.com/vercel/ncc/issues/1228)) ([84f8c52](84f8c52872))

### [`v0.38.2`](https://github.com/vercel/ncc/releases/tag/0.38.2)

[Compare Source](https://github.com/vercel/ncc/compare/0.38.1...0.38.2)

##### Bug Fixes

- **deps:** update webpack to v5.94.0, terser to v5.33.0 ([#&#8203;1213](https://github.com/vercel/ncc/issues/1213)) ([158a1fd](158a1fdcbc)), closes [#&#8203;1193](https://github.com/vercel/ncc/issues/1193) [#&#8203;1194](https://github.com/vercel/ncc/issues/1194) [#&#8203;1177](https://github.com/vercel/ncc/issues/1177) [#&#8203;1204](https://github.com/vercel/ncc/issues/1204) [#&#8203;1195](https://github.com/vercel/ncc/issues/1195)

Huge thanks to [@&#8203;theoludwig](https://github.com/theoludwig) 🎉

### [`v0.38.1`](https://github.com/vercel/ncc/releases/tag/0.38.1)

[Compare Source](https://github.com/vercel/ncc/compare/0.38.0...0.38.1)

##### Bug Fixes

- sourcemap sources removes webpack path ([#&#8203;1122](https://github.com/vercel/ncc/issues/1122)) ([ce5984e](ce5984e4b0)), closes [#&#8203;1011](https://github.com/vercel/ncc/issues/1011) [#&#8203;1121](https://github.com/vercel/ncc/issues/1121)

### [`v0.38.0`](https://github.com/vercel/ncc/releases/tag/0.38.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.37.0...0.38.0)

##### Features

- Log minification error when `--debug` ([#&#8203;1102](https://github.com/vercel/ncc/issues/1102)) ([e2779f4](e2779f4203))

### [`v0.37.0`](https://github.com/vercel/ncc/releases/tag/0.37.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.36.1...0.37.0)

##### Features

- add support for TypeScript 5.0's array extends in tsconfig ([#&#8203;1105](https://github.com/vercel/ncc/issues/1105)) ([f898f8e](f898f8ea85))

### [`v0.36.1`](https://github.com/vercel/ncc/releases/tag/0.36.1)

[Compare Source](https://github.com/vercel/ncc/compare/0.36.0...0.36.1)

##### Bug Fixes

- add missing pr title lint action ([#&#8203;1032](https://github.com/vercel/ncc/issues/1032)) ([c2d03cf](c2d03cf6db))
- add `ncc --version` and `ncc --help` ([#&#8203;1030](https://github.com/vercel/ncc/issues/1030)) ([d38b619](d38b619554))

### [`v0.36.0`](https://github.com/vercel/ncc/releases/tag/0.36.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.34.0...0.36.0)

##### Bug Fixes

- gitignore should include release.config.js ([#&#8203;1016](https://github.com/vercel/ncc/issues/1016)) ([44e2eac](44e2eac6c9))
- node 18 by update source-map used by Terser to 0.7.4 ([#&#8203;999](https://github.com/vercel/ncc/issues/999)) ([2f69f83](2f69f838aa))

##### Features

- add semantic-release to autopublish ([#&#8203;1015](https://github.com/vercel/ncc/issues/1015)) ([be3405d](be3405dbc3)), closes [#&#8203;1000](https://github.com/vercel/ncc/issues/1000)

### [`v0.34.0`](https://github.com/vercel/ncc/releases/tag/0.34.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.33.4...0.34.0)

##### Changes

Add support for TS 4.7

- Chore(deps-dev): bump ts-loader from 8.3.0 to 9.3.0: [#&#8203;921](https://github.com/vercel/ncc/issues/921)
- Chore(deps-dev): bump express from 4.17.1 to 4.18.1: [#&#8203;917](https://github.com/vercel/ncc/issues/917)
- Chore: add `memory-fs` to the devDependencies: [#&#8203;927](https://github.com/vercel/ncc/issues/927)

##### Credits

Huge thanks to [@&#8203;stscoundrel](https://github.com/stscoundrel) and [@&#8203;shogo82148](https://github.com/shogo82148) for helping!

### [`v0.33.4`](https://github.com/vercel/ncc/releases/tag/0.33.4)

[Compare Source](https://github.com/vercel/ncc/compare/0.33.3...0.33.4)

##### Changes

- Fix: Add missing variable declaration: [#&#8203;773](https://github.com/vercel/ncc/issues/773)
- Chore: add windows to CI: [#&#8203;896](https://github.com/vercel/ncc/issues/896)
- Chore: bump webpack-asset-relocator-loader to 1.7.2: [#&#8203;912](https://github.com/vercel/ncc/issues/912)
- Chore(deps-dev): bump vm2 from 3.9.4 to 3.9.6: [#&#8203;872](https://github.com/vercel/ncc/issues/872)
- Chore(deps): bump url-parse from 1.5.3 to 1.5.7: [#&#8203;875](https://github.com/vercel/ncc/issues/875)
- Chore(deps): bump url-parse from 1.5.7 to 1.5.10: [#&#8203;879](https://github.com/vercel/ncc/issues/879)
- Chore(deps-dev): bump stripe from 8.167.0 to 8.205.0: [#&#8203;882](https://github.com/vercel/ncc/issues/882)
- Chore(deps-dev): bump typescript from 4.4.2 to 4.6.2: [#&#8203;881](https://github.com/vercel/ncc/issues/881)
- Chore(deps-dev): bump twilio from 3.66.1 to 3.75.0: [#&#8203;884](https://github.com/vercel/ncc/issues/884)
- Chore(deps): bump actions/setup-node from 2 to 3: [#&#8203;880](https://github.com/vercel/ncc/issues/880)
- Chore(deps-dev): bump graphql from 15.5.1 to 15.8.0: [#&#8203;885](https://github.com/vercel/ncc/issues/885)
- Chore: replace deprecated String.prototype.substr(): [#&#8203;894](https://github.com/vercel/ncc/issues/894)
- Chore(deps): bump actions/checkout from 2 to 3: [#&#8203;902](https://github.com/vercel/ncc/issues/902)
- Chore(deps-dev): bump [@&#8203;azure/cosmos](https://github.com/azure/cosmos) from 3.12.3 to 3.15.1: [#&#8203;905](https://github.com/vercel/ncc/issues/905)
- Chore(deps-dev): bump stripe from 8.205.0 to 8.214.0: [#&#8203;906](https://github.com/vercel/ncc/issues/906)
- Chore(deps-dev): bump [@&#8203;google-cloud/bigquery](https://github.com/google-cloud/bigquery) from 5.7.0 to 5.12.0: [#&#8203;903](https://github.com/vercel/ncc/issues/903)
- Chore(deps-dev): bump tsconfig-paths from 3.10.1 to 3.14.1: [#&#8203;904](https://github.com/vercel/ncc/issues/904)

##### Credits

Huge thanks to [@&#8203;CommanderRoot](https://github.com/CommanderRoot) for helping!

### [`v0.33.3`](https://github.com/vercel/ncc/releases/tag/0.33.3)

[Compare Source](https://github.com/vercel/ncc/compare/0.33.2...0.33.3)

##### Patches

- Fix: bump license-webpack-plugin: [#&#8203;871](https://github.com/vercel/ncc/issues/871)
- Chore(deps): bump follow-redirects from 1.14.7 to 1.14.8: [#&#8203;870](https://github.com/vercel/ncc/issues/870)

### [`v0.33.2`](https://github.com/vercel/ncc/releases/tag/0.33.2)

[Compare Source](https://github.com/vercel/ncc/compare/0.33.1...0.33.2)

##### Patches

- Fix: use `sha256` instead of deprecated `md5` for hash algorithm: [#&#8203;868](https://github.com/vercel/ncc/issues/868)
- Fix: typo in build script: [#&#8203;835](https://github.com/vercel/ncc/issues/835)
- Chore(test) Add Node.js 16 to CI: [#&#8203;801](https://github.com/vercel/ncc/issues/801)
- Chore(deps): bump nodemailer from 6.5.0 to 6.7.2: [#&#8203;833](https://github.com/vercel/ncc/issues/833)
- Chore(deps-dev): bump terser from 5.7.1 to 5.10.0: [#&#8203;840](https://github.com/vercel/ncc/issues/840)
- Chore(deps-dev): bump passport from 0.4.1 to 0.5.2: [#&#8203;839](https://github.com/vercel/ncc/issues/839)
- Chore(deps-dev): bump sequelize from 6.6.5 to 6.12.4: [#&#8203;843](https://github.com/vercel/ncc/issues/843)
- Chore(deps-dev): bump analytics-node from 5.0.0 to 6.0.0: [#&#8203;838](https://github.com/vercel/ncc/issues/838)
- Chore(deps): bump follow-redirects from 1.14.5 to 1.14.7: [#&#8203;846](https://github.com/vercel/ncc/issues/846)
- Chore(deps): bump cached-path-relative from 1.0.2 to 1.1.0: [#&#8203;854](https://github.com/vercel/ncc/issues/854)
- Chore(deps-dev): bump license-webpack-plugin from 2.3.20 to 4.0.1: [#&#8203;859](https://github.com/vercel/ncc/issues/859)
- Chore(deps): bump simple-get from 3.1.0 to 3.1.1: [#&#8203;864](https://github.com/vercel/ncc/issues/864)
- Chore(deps-dev): bump aws-sdk from 2.1024.0 to 2.1068.0: [#&#8203;867](https://github.com/vercel/ncc/issues/867)

##### Credits

Huge thanks to [@&#8203;shakefu](https://github.com/shakefu) for helping!

### [`v0.33.1`](https://github.com/vercel/ncc/releases/tag/0.33.1)

[Compare Source](https://github.com/vercel/ncc/compare/0.33.0...0.33.1)

##### Patches

- Allow configuring mainFields for nccing browser modules: [#&#8203;832](https://github.com/vercel/ncc/issues/832)

### [`v0.33.0`](https://github.com/vercel/ncc/releases/tag/0.33.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.32.0...0.33.0)

##### Minor Changes

- Chore(deps-dev): bump [@&#8203;vercel/webpack-asset-relocator-loader](https://github.com/vercel/webpack-asset-relocator-loader): [#&#8203;826](https://github.com/vercel/ncc/issues/826)
- Fix: Fix source maps: [#&#8203;818](https://github.com/vercel/ncc/issues/818)
- Feat: Allow using matches from externals for regex matching: [#&#8203;825](https://github.com/vercel/ncc/issues/825)

##### Patches

- Chore(deps-dev): bump koa from 2.13.1 to 2.13.4: [#&#8203;822](https://github.com/vercel/ncc/issues/822)
- Chore(deps-dev): bump mariadb from 2.5.4 to 2.5.5: [#&#8203;823](https://github.com/vercel/ncc/issues/823)

##### Credits

Huge thanks to [@&#8203;fenix20113](https://github.com/fenix20113) for helping!

### [`v0.32.0`](https://github.com/vercel/ncc/releases/tag/0.32.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.31.1...0.32.0)

##### Changes

- Feat: bump to webpack\@&#8203;5.61.0: [#&#8203;809](https://github.com/vercel/ncc/issues/809)
- Docs: add debug command description: [#&#8203;800](https://github.com/vercel/ncc/issues/800)
- Chore(deps): bump object-path from 0.11.7 to 0.11.8: [#&#8203;778](https://github.com/vercel/ncc/issues/778)
- Chore(deps): bump tmpl from 1.0.4 to 1.0.5: [#&#8203;779](https://github.com/vercel/ncc/issues/779)
- Chore(deps-dev): bump vm2 from 3.9.3 to 3.9.4: [#&#8203;795](https://github.com/vercel/ncc/issues/795)
- Chore(deps-dev): bump axios from 0.21.1 to 0.21.2: [#&#8203;810](https://github.com/vercel/ncc/issues/810)
- Chore(deps-dev): bump aws-sdk from 2.958.0 to 2.1024.0: [#&#8203;812](https://github.com/vercel/ncc/issues/812)
- Chore(deps-dev): bump webpack from 5.61.0 to 5.62.1: [#&#8203;813](https://github.com/vercel/ncc/issues/813)
- Chore(deps): bump passport-oauth2 from 1.5.0 to 1.6.1: [#&#8203;811](https://github.com/vercel/ncc/issues/811)
- Chore(deps): bump url-parse from 1.5.1 to 1.5.3: [#&#8203;815](https://github.com/vercel/ncc/issues/815)

##### Credits

Huge thanks to [@&#8203;fireairforce](https://github.com/fireairforce) and [@&#8203;jesec](https://github.com/jesec) for helping!

### [`v0.31.1`](https://github.com/vercel/ncc/releases/tag/0.31.1)

[Compare Source](https://github.com/vercel/ncc/compare/0.31.0...0.31.1)

##### Patches

- Fix `tsconfig.json` detection: [#&#8203;770](https://github.com/vercel/ncc/issues/770)

### [`v0.31.0`](https://github.com/vercel/ncc/releases/tag/0.31.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.30.0...0.31.0)

##### Changes

- Fix `compilerOptions` from `tsconfig.json`: [#&#8203;766](https://github.com/vercel/ncc/issues/766)
- Bump typescript to 4.4.2: [#&#8203;767](https://github.com/vercel/ncc/issues/767)
- Chore(deps-dev): bump graceful-fs from 4.2.6 to 4.2.8: [#&#8203;761](https://github.com/vercel/ncc/issues/761)
- Chore(deps): bump tar from 4.4.15 to 4.4.19: [#&#8203;763](https://github.com/vercel/ncc/issues/763)
- Chore(deps): bump object-path from 0.11.5 to 0.11.7: [#&#8203;764](https://github.com/vercel/ncc/issues/764)

### [`v0.30.0`](https://github.com/vercel/ncc/releases/tag/0.30.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.29.2...0.30.0)

##### Changes

- Major: Change asset builds to opt-in with new option `--asset-builds`: [#&#8203;756](https://github.com/vercel/ncc/issues/756)
- Chore: bump typescript from 3.9.9 to 4.3.5: [#&#8203;739](https://github.com/vercel/ncc/issues/739)
- Chore: bump codecov to 3.8.3: [#&#8203;752](https://github.com/vercel/ncc/issues/752)

##### Description

Previous, `fs.readFile('./asset.js')` would compile `asset.js` instead of including as an asset.

With this release, the default behavior has been changed to include `asset.js` as an asset only.

If you want the old behavior, you can use the `--asset-builds` option.

##### Credits

Huge thanks to [@&#8203;guybedford](https://github.com/guybedford) for helping!

### [`v0.29.2`](https://github.com/vercel/ncc/releases/tag/0.29.2)

[Compare Source](https://github.com/vercel/ncc/compare/0.29.1...0.29.2)

##### Patches

- Fix: ensure nested builds of `__nccwpck_require__`: [#&#8203;751](https://github.com/vercel/ncc/issues/751)

##### Credits

Huge thanks to [@&#8203;guybedford](https://github.com/guybedford) for helping!

### [`v0.29.1`](https://github.com/vercel/ncc/releases/tag/0.29.1)

[Compare Source](https://github.com/vercel/ncc/compare/0.29.0...0.29.1)

##### Patches

- Fix: add stringify-loader: [#&#8203;742](https://github.com/vercel/ncc/issues/742)
- Fix: package.json asset type module setting: [#&#8203;733](https://github.com/vercel/ncc/issues/733)
- Chore(deps): update dependencies: [#&#8203;736](https://github.com/vercel/ncc/issues/736)
- Chore(deps): bump tar from 4.4.13 to 4.4.15: [#&#8203;743](https://github.com/vercel/ncc/issues/743)
- Chore(deps): bump path-parse from 1.0.6 to 1.0.7: [#&#8203;745](https://github.com/vercel/ncc/issues/745)
- Chore(deps-dev): bump pdfkit from 0.12.1 to 0.12.3: [#&#8203;740](https://github.com/vercel/ncc/issues/740)

##### Credits

Huge thanks to [@&#8203;guybedford](https://github.com/guybedford), [@&#8203;mmorel-35](https://github.com/mmorel-35), and [@&#8203;jpcloureiro](https://github.com/jpcloureiro) for helping!

### [`v0.29.0`](https://github.com/vercel/ncc/releases/tag/0.29.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.28.6...0.29.0)

##### Changes

- Major: output ESM for `.mjs` or `type=module` builds: [#&#8203;720](https://github.com/vercel/ncc/issues/720)
- Feat: update to webpack-asset-reloactor-loader\@&#8203;1.5.0: [#&#8203;718](https://github.com/vercel/ncc/issues/718)
- Feat: update to webpack\@&#8203;5.42.0: [#&#8203;723](https://github.com/vercel/ncc/issues/723)
- Feat: update to webpack\@&#8203;5.43.0: [#&#8203;724](https://github.com/vercel/ncc/issues/724)
- Chore: bump set-getter from 0.1.0 to 0.1.1: [#&#8203;719](https://github.com/vercel/ncc/issues/719)
- Fix: typo in readme: [#&#8203;712](https://github.com/vercel/ncc/issues/712)

##### Credits

Huge thanks to [@&#8203;rethab](https://github.com/rethab) and [@&#8203;guybedford](https://github.com/guybedford) for helping!

### [`v0.28.6`](https://github.com/vercel/ncc/releases/tag/0.28.6)

[Compare Source](https://github.com/vercel/ncc/compare/0.28.5...0.28.6)

##### Patches

- Fix: Update to webpack\@&#8203;5.36.2: [#&#8203;707](https://github.com/vercel/ncc/issues/707)
- Fix: Update ts-loader to fix webpack warning: [#&#8203;710](https://github.com/vercel/ncc/issues/710)
- Deps: Bump hosted-git-info from 2.8.8 to 2.8.9: [#&#8203;708](https://github.com/vercel/ncc/issues/708)

##### Credits

Huge thanks to [@&#8203;adriencohen](https://github.com/adriencohen) and [@&#8203;huozhi](https://github.com/huozhi) for helping!

### [`v0.28.5`](https://github.com/vercel/ncc/releases/tag/0.28.5)

[Compare Source](https://github.com/vercel/ncc/compare/0.28.4...0.28.5)

##### Patches

- Fix: handle terser error: [#&#8203;703](https://github.com/vercel/ncc/issues/703)
- Fix: treat compilation.errors as a set: [#&#8203;705](https://github.com/vercel/ncc/issues/705)
- Fix: unify `target` arg description, add `transpile-only` arg to readme: [#&#8203;702](https://github.com/vercel/ncc/issues/702)

##### Credits

Huge thanks to [@&#8203;guybedford](https://github.com/guybedford) and [@&#8203;Simek](https://github.com/Simek) for helping!

### [`v0.28.4`](https://github.com/vercel/ncc/releases/tag/0.28.4)

[Compare Source](https://github.com/vercel/ncc/compare/0.28.3...0.28.4)

##### Patches

- Fix: Adjust caching to use hashes: [#&#8203;698](https://github.com/vercel/ncc/issues/698)
- Fix: support top-level await: [#&#8203;700](https://github.com/vercel/ncc/issues/700)
- Fix: publish should build without cache: [#&#8203;701](https://github.com/vercel/ncc/issues/701)
- Chore:  redis from 2.8.0 to 3.1.1: [#&#8203;699](https://github.com/vercel/ncc/issues/699)
- Chore: Bump ssri from 6.0.1 to 6.0.2: [#&#8203;695](https://github.com/vercel/ncc/issues/695)
- Chore: rename master to main: [#&#8203;694](https://github.com/vercel/ncc/issues/694)

##### Credits

Huge thanks to [@&#8203;guybedford](https://github.com/guybedford) for helping!

### [`v0.28.3`](https://github.com/vercel/ncc/releases/tag/0.28.3)

[Compare Source](https://github.com/vercel/ncc/compare/0.28.2...0.28.3)

##### Patches

- Fix: lock license plugin version: [#&#8203;692](https://github.com/vercel/ncc/issues/692)

##### Credits

Huge thanks to [@&#8203;huozhi](https://github.com/huozhi) for helping!

### [`v0.28.2`](https://github.com/vercel/ncc/releases/tag/0.28.2)

[Compare Source](https://github.com/vercel/ncc/compare/0.28.1...0.28.2)

##### Patches

- Fix: unknown compiler option `incremental`: [#&#8203;685](https://github.com/vercel/ncc/issues/685)
- Fix: replace .npmignore with "files" prop: [#&#8203;688](https://github.com/vercel/ncc/issues/688)

##### Credits

Huge thanks to [@&#8203;Songkeys](https://github.com/Songkeys) for helping!

### [`v0.28.1`](https://github.com/vercel/ncc/releases/tag/0.28.1)

[Compare Source](https://github.com/vercel/ncc/compare/0.28.0...0.28.1)

##### Patches

- Fix: Rebuild bundle to fix [#&#8203;684](https://github.com/vercel/ncc/issues/684)
- Deps: Bump codecov to 3.8.1: [#&#8203;683](https://github.com/vercel/ncc/issues/683)

### [`v0.28.0`](https://github.com/vercel/ncc/releases/tag/0.28.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.27.0...0.28.0)

##### Minor Changes

- Feat: `exports` conditions semantics: [#&#8203;665](https://github.com/vercel/ncc/issues/665)
- Feat: `imports` support, webpack upgrade: [#&#8203;672](https://github.com/vercel/ncc/issues/672)
- Feat: Support Regexp externals: [#&#8203;654](https://github.com/vercel/ncc/issues/654)
- Fix: TS interop test and fix: [#&#8203;671](https://github.com/vercel/ncc/issues/671)
- Fix: Upgrade local TS: [#&#8203;674](https://github.com/vercel/ncc/issues/674)
- Chore: Interop test case: [#&#8203;667](https://github.com/vercel/ncc/issues/667)
- Chore: add console output when minifying fails: [#&#8203;648](https://github.com/vercel/ncc/issues/648)
- Chore: Add Node.js 14 to CI: [#&#8203;659](https://github.com/vercel/ncc/issues/659)
- Docs: Fix out \[file] → out \[dir]: [#&#8203;675](https://github.com/vercel/ncc/issues/675)
- Deps: Update to webpack\@&#8203;5.30.0: [#&#8203;681](https://github.com/vercel/ncc/issues/681)
- Deps: Update to webpack\@&#8203;5.26.3: [#&#8203;664](https://github.com/vercel/ncc/issues/664)
- Deps: Update to webpack\@&#8203;5.24.4: [#&#8203;658](https://github.com/vercel/ncc/issues/658)
- Deps: Update to webpack-asset-relocator-loader\@&#8203;1.3.0: [#&#8203;682](https://github.com/vercel/ncc/issues/682)
- Deps: Upgrade to webpack-asset-relocator-loader\@&#8203;1.2.4: [#&#8203;673](https://github.com/vercel/ncc/issues/673)
- Deps: Update to webpack-asset-relocator\@&#8203;1.2.3: [#&#8203;662](https://github.com/vercel/ncc/issues/662)
- Deps: Upgrade to terser\@&#8203;5.6.1: [#&#8203;669](https://github.com/vercel/ncc/issues/669)
- Deps: Bump socket.io from 2.2.0 to 2.4.0: [#&#8203;645](https://github.com/vercel/ncc/issues/645)
- Deps: Bump pug from 2.0.3 to 3.0.1: [#&#8203;656](https://github.com/vercel/ncc/issues/656)
- Deps: Bump elliptic from 6.5.3 to 6.5.4: [#&#8203;657](https://github.com/vercel/ncc/issues/657)
- Deps: Bump msgpack5 from 4.2.1 to 4.5.1: [#&#8203;660](https://github.com/vercel/ncc/issues/660)
- Deps: Bump y18n from 3.2.1 to 3.2.2: [#&#8203;678](https://github.com/vercel/ncc/issues/678)

##### Credits

Huge thanks to [@&#8203;guybedford](https://github.com/guybedford), [@&#8203;Songkeys](https://github.com/Songkeys), [@&#8203;adriencohen](https://github.com/adriencohen), and [@&#8203;huozhi](https://github.com/huozhi) for helping!

### [`v0.27.0`](https://github.com/vercel/ncc/releases/tag/0.27.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.26.2...0.27.0)

##### Changes

- Feat: support customEmit ncc option: [#&#8203;634](https://github.com/vercel/ncc/issues/634)
- Fix: correct declaration output dir: [#&#8203;627](https://github.com/vercel/ncc/issues/627)
- Update to webpack-asset-relocator\@&#8203;1.2.0: [#&#8203;640](https://github.com/vercel/ncc/issues/640)

##### Credits

Huge thanks to [@&#8203;guybedford](https://github.com/guybedford) and [@&#8203;zeroooooooo](https://github.com/zeroooooooo) for helping!

### [`v0.26.2`](https://github.com/vercel/ncc/releases/tag/0.26.2)

[Compare Source](https://github.com/vercel/ncc/compare/0.26.1...0.26.2)

##### Patches

- Enable minification for sourcemap-register.js: [#&#8203;631](https://github.com/vercel/ncc/issues/631)
- Avoid **webpack\_require** conflicts: [#&#8203;633](https://github.com/vercel/ncc/issues/633)
- Bump axios from 0.18.1 to 0.21.1: [#&#8203;636](https://github.com/vercel/ncc/issues/636)
- Fix: skip typechecking on sub-builds: [#&#8203;637](https://github.com/vercel/ncc/issues/637)

##### Credits

Huge thanks to [@&#8203;xom9ikk](https://github.com/xom9ikk) and [@&#8203;guybedford](https://github.com/guybedford) for helping!

### [`v0.26.1`](https://github.com/vercel/ncc/releases/tag/0.26.1)

[Compare Source](https://github.com/vercel/ncc/compare/0.26.0...0.26.1)

##### Patches

- Ensure separate asset compilation states in subbuilds: [#&#8203;630](https://github.com/vercel/ncc/issues/630)

##### Credits

Huge thanks to [@&#8203;guybedford](https://github.com/guybedford) for helping!

### [`v0.26.0`](https://github.com/vercel/ncc/releases/tag/0.26.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.25.1...0.26.0)

##### Changes

- Asset subbundle builds: [#&#8203;625](https://github.com/vercel/ncc/issues/625)
- Bump ini from 1.3.5 to 1.3.7: [#&#8203;624](https://github.com/vercel/ncc/issues/624)
- Update example with missing TypeScript dependency: [#&#8203;623](https://github.com/vercel/ncc/issues/623)
- Update readme with missing TS and ES options: [#&#8203;615](https://github.com/vercel/ncc/issues/615)

##### Credits

Huge thanks to [@&#8203;restuwahyu13](https://github.com/restuwahyu13) and [@&#8203;guybedford](https://github.com/guybedford) for helping!

### [`v0.25.1`](https://github.com/vercel/ncc/releases/tag/0.25.1)

[Compare Source](https://github.com/vercel/ncc/compare/0.25.0...0.25.1)

##### Changes

- Allow passing `target`: [#&#8203;614](https://github.com/vercel/ncc/issues/614)

##### Credits

Huge thanks to [@&#8203;ijjk](https://github.com/ijjk) for helping!

### [`v0.25.0`](https://github.com/vercel/ncc/releases/tag/0.25.0)

[Compare Source](https://github.com/vercel/ncc/compare/0.24.1...0.25.0)

##### Changes

- Bump `webpack` from `5.0.0-beta.28` to `5.2.0`: [#&#8203;602](https://github.com/vercel/ncc/issues/602)
- Bump `npm-user-validate` from `1.0.0` to `1.0.1`: [#&#8203;604](https://github.com/vercel/ncc/issues/604)
- Bump `object-path` from `0.11.4` to `0.11.5`: [#&#8203;607](https://github.com/vercel/ncc/issues/607)
- Fix `--quiet` flag on ts build: [#&#8203;605](https://github.com/vercel/ncc/issues/605)
- Add integration test for `@slack/web-api`: [#&#8203;591](https://github.com/vercel/ncc/issues/591)

##### Credits

Huge thanks to [@&#8203;huozhi](https://github.com/huozhi) and [@&#8203;ataylorme](https://github.com/ataylorme) for helping!

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNTAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjE1MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/881
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-01 09:00:34 +00:00
Renovate Bot
b1c873a66b chore(deps): update dependency @actions/core to v1.11.1 (#880)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [@actions/core](https://github.com/actions/toolkit/tree/main/packages/core) ([source](https://github.com/actions/toolkit/tree/HEAD/packages/core)) | [`1.10.0` → `1.11.1`](https://renovatebot.com/diffs/npm/@actions%2fcore/1.10.0/1.11.1) | ![age](https://developer.mend.io/api/mc/badges/age/npm/@actions%2fcore/1.11.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/@actions%2fcore/1.10.0/1.11.1?slim=true) |

---

### Release Notes

<details>
<summary>actions/toolkit (@&#8203;actions/core)</summary>

### [`v1.11.1`](https://github.com/actions/toolkit/blob/HEAD/packages/core/RELEASES.md#1111)

- Fix uses of `crypto.randomUUID` on Node 18 and earlier [#&#8203;1842](https://github.com/actions/toolkit/pull/1842)

##### 1.11.0

- Add platform info utilities [#&#8203;1551](https://github.com/actions/toolkit/pull/1551)
- Remove dependency on `uuid` package [#&#8203;1824](https://github.com/actions/toolkit/pull/1824)

##### 1.10.1

- Fix error message reference in oidc utils [#&#8203;1511](https://github.com/actions/toolkit/pull/1511)

##### 1.10.0

- `saveState` and `setOutput` now use environment files if available [#&#8203;1178](https://github.com/actions/toolkit/pull/1178)
- `getMultilineInput` now correctly trims whitespace by default [#&#8203;1185](https://github.com/actions/toolkit/pull/1185)

##### 1.9.1

- Randomize delimiter when calling `core.exportVariable`

##### 1.9.0

- Added `toPosixPath`, `toWin32Path` and `toPlatformPath` utilities [#&#8203;1102](https://github.com/actions/toolkit/pull/1102)

##### 1.8.2

- Update to v2.0.1 of `@actions/http-client` [#&#8203;1087](https://github.com/actions/toolkit/pull/1087)

##### 1.8.1

- Update to v2.0.0 of `@actions/http-client`

##### 1.8.0

- Deprecate `markdownSummary` extension export in favor of `summary`
  - [#&#8203;1072](https://github.com/actions/toolkit/pull/1072)
  - [#&#8203;1073](https://github.com/actions/toolkit/pull/1073)

##### 1.7.0

- [Added `markdownSummary` extension](https://github.com/actions/toolkit/pull/1014)

##### 1.6.0

- [Added OIDC Client function `getIDToken`](https://github.com/actions/toolkit/pull/919)
- [Added `file` parameter to `AnnotationProperties`](https://github.com/actions/toolkit/pull/896)

##### 1.5.0

- [Added support for notice annotations and more annotation fields](https://github.com/actions/toolkit/pull/855)

##### 1.4.0

- [Added the `getMultilineInput` function](https://github.com/actions/toolkit/pull/829)

##### 1.3.0

- [Added the trimWhitespace option to getInput](https://github.com/actions/toolkit/pull/802)
- [Added the getBooleanInput function](https://github.com/actions/toolkit/pull/725)

##### 1.2.7

- [Prepend newline for set-output](https://github.com/actions/toolkit/pull/772)

##### 1.2.6

- [Update `exportVariable` and `addPath` to use environment files](https://github.com/actions/toolkit/pull/571)

##### 1.2.5

- [Correctly bundle License File with package](https://github.com/actions/toolkit/pull/548)

##### 1.2.4

- [Be more lenient in accepting non-string command inputs](https://github.com/actions/toolkit/pull/405)
- [Add Echo commands](https://github.com/actions/toolkit/pull/411)

##### 1.2.3

- [IsDebug logging](README.md#logging)

##### 1.2.2

- [Fix escaping for runner commands](https://github.com/actions/toolkit/pull/302)

##### 1.2.1

- [Remove trailing comma from commands](https://github.com/actions/toolkit/pull/263)
- [Add "types" to package.json](https://github.com/actions/toolkit/pull/221)

##### 1.2.0

- saveState and getState functions for wrapper tasks (on finally entry points that run post job)

##### 1.1.3

- setSecret added to register a secret with the runner to be masked from the logs
- exportSecret which was not implemented and never worked was removed after clarification from product.

##### 1.1.1

- Add support for action input variables with multiple spaces [#&#8203;127](https://github.com/actions/toolkit/issues/127)
- Switched ## commands to :: commands (should have no noticeable impact) \[[#&#8203;110](https://github.com/actions/toolkit/issues/110))([#&#8203;110](https://github.com/actions/toolkit/pull/110))

##### 1.1.0

- Added helpers for `group` and `endgroup` [#&#8203;98](https://github.com/actions/toolkit/pull/98)

##### 1.0.0

- Initial release

### [`v1.11.0`](https://github.com/actions/toolkit/blob/HEAD/packages/core/RELEASES.md#1110)

- Add platform info utilities [#&#8203;1551](https://github.com/actions/toolkit/pull/1551)
- Remove dependency on `uuid` package [#&#8203;1824](https://github.com/actions/toolkit/pull/1824)

### [`v1.10.1`](https://github.com/actions/toolkit/blob/HEAD/packages/core/RELEASES.md#1101)

- Fix error message reference in oidc utils [#&#8203;1511](https://github.com/actions/toolkit/pull/1511)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNTAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjE1MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/runner/pulls/880
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-05-01 08:48:49 +00:00
Renovate Bot
1d6e7879c8 fix(deps): update module github.com/rhysd/actionlint to v1.7.12 (#873)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/rhysd/actionlint](https://github.com/rhysd/actionlint) | `v1.7.11` → `v1.7.12` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2frhysd%2factionlint/v1.7.12?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2frhysd%2factionlint/v1.7.11/v1.7.12?slim=true) |

---

### Release Notes

<details>
<summary>rhysd/actionlint (github.com/rhysd/actionlint)</summary>

### [`v1.7.12`](https://github.com/rhysd/actionlint/blob/HEAD/CHANGELOG.md#v1712---2026-03-30)

[Compare Source](https://github.com/rhysd/actionlint/compare/v1.7.11...v1.7.12)

- Support the [`timezone` configuration in `on.schedule`](https://docs.github.com/en/actions/reference/workflows-and-actions/workflow-syntax#onschedule) with checks for IANA timezone string. See the [documentation](https://github.com/rhysd/actionlint/blob/main/docs/checks.md#check-cron-syntax-and-timezone) for more details. Note that actionlint starts to embed the timezone database in the executables from this version so the binary sizes slightly increase. ([#&#8203;641](https://github.com/rhysd/actionlint/issues/641), thanks [@&#8203;martincostello](https://github.com/martincostello))
  ```yaml
  on:
    schedule:
      # ERROR: The timezone is not a valid IANA timezone string
      - cron: '*/5 * * * *'
        timezone: 'Asia/Somewhere'
  ```
- Support the [`jobs.<job_name>.environment.deployment` configuration](https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/control-deployments#using-environments-without-deployments). ([#&#8203;639](https://github.com/rhysd/actionlint/issues/639), thanks [@&#8203;springmeyer](https://github.com/springmeyer))
- Support the [`macos-26-intel` runner label](https://github.blog/changelog/2026-02-26-macos-26-is-now-generally-available-for-github-hosted-runners/). ([#&#8203;629](https://github.com/rhysd/actionlint/issues/629), thanks [@&#8203;hugovk](https://github.com/hugovk))
- Fix the [table of webhook activity types](https://github.com/rhysd/actionlint/blob/main/all_webhooks.go) are outdated by rebuilding the [script to scrape the table](https://github.com/rhysd/actionlint/tree/main/scripts/generate-webhook-events) from scratch.
- Support Go 1.26 and drop the support for Go 1.24. Now supported versions are 1.25 and 1.26.
- Tests are run on arm64 Windows in CI.
- Update the popular actions data set to the latest.

\[Changes]\[v1.7.12]

<a id="v1.7.11"></a>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNTAuMCIsInVwZGF0ZWRJblZlciI6IjQzLjE1MC4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/873
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-04-30 21:03:33 +00:00
Lunny Xiao
13dc9386fe Rename act_runner to runner (#850)
## Consumer-facing breaking changes

- **Go module path**: `gitea.com/gitea/act_runner` → `gitea.com/gitea/runner`. Anything importing `act/...` or `internal/...` packages (notably Gitea itself) must update imports.
- **Binary name**: `act_runner` → `gitea-runner`. Wrapper scripts, systemd units, init scripts, and documentation referencing the binary by `act_runner` will break.
- **Docker image**: `gitea/act_runner` → `gitea/runner` (incl. `*-dind-rootless` variants). Users pulling `gitea/act_runner:nightly` etc. will get stale images. Note: the image name is `gitea/runner`, not `gitea/gitea-runner`.
- **Release artifact paths**: S3 directory `act_runner/{{.Version}}` → `gitea-runner/{{.Version}}`, and artifact filenames change with the new project name. Existing download URLs break.
- **Metrics namespace**: changed from `act_runner` to `gitea_runner` (e.g. `act_runner_jobs_total` → `gitea_runner_jobs_total`); existing monitors/dashboards must be updated.
- **ldflags version path**: `gitea.com/gitea/act_runner/internal/pkg/ver.version` → `gitea.com/gitea/runner/internal/pkg/ver.version`. Affects anyone building with custom ldflags.
- **Kubernetes example resource names**: `act-runner` / `act-runner-vol` → `runner` / `runner-vol`. Users who copied the manifests verbatim will see resource churn on apply.
- **s6 service name**: `scripts/s6/act_runner/` → `scripts/s6/gitea-runner/` (image-internal; only matters for downstream image overrides).

Unchanged: YAML config field names, env vars (`GITEA_*`), CLI flags/subcommands, registration file format.
---------

Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/850
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-04-30 20:12:51 +00:00
Nicolas
8e6b3be96a docs: Update docs (#872)
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/872
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Nicolas <bircni@icloud.com>
Co-committed-by: Nicolas <bircni@icloud.com>
2026-04-30 18:29:59 +00:00
Bo-Yi Wu
e5e53c732e perf(config): lower default fetch_interval_max from 60s to 5s (#875)
## Summary

- Lower the default `fetch_interval_max` from 60s to 5s to reduce job pickup latency for common single-runner deployments
- PR #819 optimized defaults for 200-runner fleets, regressing single-runner pickup time from ~2s to ~65s
- Most deployments use few runners; large fleets can still tune this value higher in their config

## Impact

| `fetch_interval_max` | 1 runner pickup | 200 runners idle |
| -------------------- | --------------- | ---------------- |
| 60s (previous)       | up to **65s**   | 3.3 req/s        |
| **5s (new default)** | up to **5s**    | 40 req/s         |

Closes https://gitea.com/gitea/act_runner/issues/869

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/875
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2026-04-30 17:40:35 +00:00
silverwind
2516573592 chore: clean up nolint directives in act package (#864)
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>
2026-04-29 18:32:55 +00:00
Renovate Bot
35834bf817 fix(deps): update module github.com/moby/patternmatcher to v0.6.1 (#868)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/moby/patternmatcher](https://github.com/moby/patternmatcher) | `v0.6.0` → `v0.6.1` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fmoby%2fpatternmatcher/v0.6.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fmoby%2fpatternmatcher/v0.6.0/v0.6.1?slim=true) |

---

### Release Notes

<details>
<summary>moby/patternmatcher (github.com/moby/patternmatcher)</summary>

### [`v0.6.1`](https://github.com/moby/patternmatcher/releases/tag/v0.6.1)

[Compare Source](https://github.com/moby/patternmatcher/compare/v0.6.0...v0.6.1)

#### What's Changed

- fix panic /  nil pointer dereference on invalid patterns [#&#8203;9](https://github.com/moby/patternmatcher/pull/9)
- ci: update actions and test against "oldest", "oldstable" and "stable" [#&#8203;8](https://github.com/moby/patternmatcher/pull/8)

**Full Changelog**: <https://github.com/moby/patternmatcher/compare/v0.6.0...v0.6.1>

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDUuMCIsInVwZGF0ZWRJblZlciI6IjQzLjE0NS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/868
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-04-29 00:47:19 +00:00
Renovate Bot
11a5dc8936 fix(deps): update module github.com/docker/docker to v25.0.15+incompatible (#867)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/docker/docker](https://github.com/docker/docker) | `v25.0.14+incompatible` → `v25.0.15+incompatible` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fdocker%2fdocker/v25.0.15+incompatible?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fdocker%2fdocker/v25.0.14+incompatible/v25.0.15+incompatible?slim=true) |

---

### Release Notes

<details>
<summary>docker/docker (github.com/docker/docker)</summary>

### [`v25.0.15+incompatible`](https://github.com/docker/docker/compare/v25.0.14...v25.0.15)

[Compare Source](https://github.com/docker/docker/compare/v25.0.14...v25.0.15)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDUuMCIsInVwZGF0ZWRJblZlciI6IjQzLjE0NS4wIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/867
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-04-29 00:46:57 +00:00
Zettat123
f09fafcb0a Clone different git repos in parallel via per-directory locks (#866)
Old `cloneLock` is a package-level `sync.Mutex` that serialized every action clone across all goroutines, regardless of target directory.
This PR replaces it with a `sync.Map` of per-directory mutexes keyed by `input.Dir`. Same-directory operations still serialize; different directories now clone in parallel.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/866
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2026-04-28 21:39:41 +00:00
Morgan Peyre
801e5cf4d5 fix: avoid 'filename too long' on matrix jobs by hashing container names (#853)
## Bug fixes

- Fixed "file name too long" errors when running matrix jobs with many or
  long-valued matrix parameters. Docker volume and container names are now
  bounded to a safe length by hashing the full name with SHA-256, keeping
  the result well within the filesystem `NAME_MAX` limit (255 bytes) even
  after Docker appends its own suffixes (`-env`, `-network`, etc.).

## Upgrade notes

This change renames the Docker containers, volumes and networks created by
the runner. Resources created by a previous version will **not** be cleaned
up automatically after upgrade and will become orphans.

After upgrading, you can remove the legacy resources with:

```sh
# volumes
docker volume ls -q | grep -E '^GITEA-ACTIONS-TASK-[0-9]+_' | xargs -r docker volume rm

# networks
docker network ls --format '{{.Name}}' | grep -E '^GITEA-ACTIONS-TASK-[0-9]+_.*-network$' | xargs -r docker network rm
```

> **Note:** If multiple act_runner instances share the same Docker daemon,
> make sure no runner using the old version is running before executing the
> cleanup commands above.

Fixes #686

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/853
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Morgan Peyre <195218+peyremorgan@noreply.gitea.com>
Co-committed-by: Morgan Peyre <195218+peyremorgan@noreply.gitea.com>
2026-04-28 00:30:27 +00:00
Renovate Bot
3f05040438 fix(deps): update module github.com/mattn/go-isatty to v0.0.22 (#863)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) | `v0.0.20` → `v0.0.22` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fmattn%2fgo-isatty/v0.0.22?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fmattn%2fgo-isatty/v0.0.20/v0.0.22?slim=true) |

---

### Release Notes

<details>
<summary>mattn/go-isatty (github.com/mattn/go-isatty)</summary>

### [`v0.0.22`](https://github.com/mattn/go-isatty/compare/v0.0.21...v0.0.22)

[Compare Source](https://github.com/mattn/go-isatty/compare/v0.0.21...v0.0.22)

### [`v0.0.21`](https://github.com/mattn/go-isatty/compare/v0.0.20...v0.0.21)

[Compare Source](https://github.com/mattn/go-isatty/compare/v0.0.20...v0.0.21)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuNiIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS42IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/863
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-04-28 00:15:16 +00:00
Renovate Bot
59d90bff26 fix(deps): update module github.com/docker/docker to v25.0.14+incompatible (#862)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/docker/docker](https://github.com/docker/docker) | `v25.0.13+incompatible` → `v25.0.14+incompatible` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fdocker%2fdocker/v25.0.14+incompatible?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fdocker%2fdocker/v25.0.13+incompatible/v25.0.14+incompatible?slim=true) |

---

### Release Notes

<details>
<summary>docker/docker (github.com/docker/docker)</summary>

### [`v25.0.14+incompatible`](https://github.com/docker/docker/compare/v25.0.13...v25.0.14)

[Compare Source](https://github.com/docker/docker/compare/v25.0.13...v25.0.14)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuNiIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS42IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/862
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-04-28 00:14:44 +00:00
silverwind
5edc4ba550 Authenticate cache requests via ACTIONS_RUNTIME_TOKEN and scope by repo (#849)
Closes #848. Addresses [GHSA-82g9-637c-2fx2](https://github.com/go-gitea/gitea/security/advisories/GHSA-82g9-637c-2fx2) and the follow-up points raised by @ChristopherHX and @haroutp in that thread.

The change is breaking only for `cache.external_server` which uses auth via a pre-shared secret.

## How auth works now

1. **Runner starts** → opens the embedded cache server on `:port`. Loads / creates a 32-byte HMAC signing key in `<cache-dir>/.secret`.
2. **Runner receives a task** → calls `handler.RegisterJob(ACTIONS_RUNTIME_TOKEN, repository)` before the job runs, defers a revoker that removes the credential on completion. Registrations are reference-counted so a stray re-register cannot revoke a live job.
3. **Job container runs `actions/cache`** → the toolkit sends `Authorization: Bearer $ACTIONS_RUNTIME_TOKEN` on every management call (`reserve`, `upload`, `commit`, `find`, `clean`). The cache server's middleware looks the token up in the registered-jobs map: miss → 401; hit → the job's repository is injected into the request context.
4. **Repository scoping** — every cache entry is stamped with `Repo` on reserve; `find`, `upload`, `commit` all verify the caller's repo matches. A job in repo A cannot see or poison a cache entry owned by repo B, even when both reach the server over the same docker bridge. GC dedup also groups by `(Repo, Key, Version)` so one repo can't age out another.
5. **Archive downloads** — `@actions/cache` does not attach Authorization when downloading `archiveLocation`, so the `find` response is a short-lived HMAC-signed URL: `…/artifacts/:id?exp=<unix>&sig=<hmac>`, 10-minute TTL, signature binds `cacheID:exp`. Tampered, expired, or foreign-secret URLs get 401.
6. **Defence-in-depth** — `ACTIONS_RUNTIME_TOKEN` is added to `task.Secrets` so the runner's log masker scrubs it from step output.

## `cache.external_server` (standalone `act_runner cache-server`)

Operators set `cache.external_secret` to the same value on the runner config and the `act_runner cache-server` config. The `cache-server` then runs with bearer auth on the cache API and exposes a control-plane at `POST /_internal/{register,revoke}` (gated by the shared secret). The runner pre-registers each task's `ACTIONS_RUNTIME_TOKEN` with the remote server before the job runs and revokes it on completion. Same per-job auth + repo scoping as the embedded handler, just over the network.

`cache-server` refuses to start without `cache.external_secret`; runner config load also fails when `cache.external_server` is set without `cache.external_secret`.

## User-facing changes

- **One-time cache miss after upgrade.** Pre-existing entries in `bolt.db` have no `Repo` stamp and won't match any job — they'll be evicted by the normal GC. First job per cache key rebuilds its cache.
- **`cache.external_server` deployments must add `cache.external_secret`.** Breaking change for anyone running a standalone `act_runner cache-server`: set the same `cache.external_secret` in both the runner config and the cache-server config. Without it neither side starts.
- **No config changes required for the default setup.** Runners using the embedded cache server (the common case) keep working without any yaml edits; the auth mechanism is invisible to workflows.

---
This PR was written with the help of Claude Opus 4.7

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/849
Reviewed-by: ChristopherHX <38043+christopherhx@noreply.gitea.com>
2026-04-27 23:59:20 +00:00
Nicolas
547a0ff297 feat: show run command, shell and env in collapsible group before step output (#847)
## Summary

Mirrors the GitHub Actions runner behaviour where each `run:` step shows a collapsible **"Run \<command\>"** section containing the script, shell command, and environment variables before the actual step output.

### What changes

- **`pkg/runner/step_run.go`**: In `stepRun.main()`, two new executors are added to the pipeline:
  1. `logRunGroupHeader()` — runs after `setupShellCommandExecutor()` (so `sr.cmdline` is already resolved). Emits a `::group::Run <step>` log entry followed by the interpolated script, the full shell command line, and the step's env vars (sorted, internal vars filtered out).
  2. The existing execution function now has `defer rawLogger.Infof("::endgroup::")` so the group is closed after the step finishes, regardless of success or failure.

### Env var filtering

Internal runner vars are hidden (`GITHUB_*`, `GITEA_*`, `RUNNER_*`, `INPUT_*`, `PATH`, `HOME`) — only user-relevant vars are shown, matching what GitHub Actions displays.

### Example output

```
▼ Run cargo build
  cargo build
  shell: bash --noprofile --norc -e -o pipefail {0}
  env:
    CARGO_HOME: /home/runner/.cargo
    CARGO_INCREMENTAL: 0
    CARGO_TERM_COLOR: always
  <actual build output>
```

---------

Co-authored-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/847
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Reviewed-by: ChristopherHX <38043+christopherhx@noreply.gitea.com>
2026-04-27 16:31:56 +00:00
Mirko Sekulic
f2b4dbf05f run docker step in host mode (#857)
## Problem
In host executor mode, uses: docker://<image> step actions fail because
act/runner/step_docker.go always attaches the step container to the job
container's network namespace, which doesn't exist in host mode.

### Example
Run following job in host runner

```yaml
jobs:
  test:
    runs-on: ubuntu-latest-host
    steps:
      - uses: docker://alpine:3.20
        with:
          args: echo hello
```
```
Error:
  failed to start container: Error response from daemon:
    joining network namespace of container:
    No such container: xxxxxx
```

This pr allows the docker step in the host mode

## Testing
I tested following steps on host runner and it worked

```yaml

 - name: Test azure cli action in host mode
   uses: azure/cli@v2
   env:
     RUNNER_OS: Linux
   with:
     inlineScript: echo "hello from azure cli"
 
 - uses: docker://alpine:3.20
   with:
     args: echo hello

```

---------

Co-authored-by: Nicolas <bircni@icloud.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/857
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: Mirko Sekulic <misha.sekulic@gmail.com>
Co-committed-by: Mirko Sekulic <misha.sekulic@gmail.com>
2026-04-27 15:26:31 +00:00
silverwind
bad4239d18 chore(deps): drop unused distribution/reference replace directive (#858)
The `replace github.com/distribution/reference v0.6.0 => v0.5.0` was added defensively in [c4b57fbc](c4b57fbcb2) (#775) against `github.com/docker/distribution v2.8.3`, whose `reference_deprecated.go` calls the now-removed `reference.SplitHostname`. However, nothing in act_runner's build graph imports `github.com/docker/distribution/reference`, so the file is never compiled and the replace has no effect.

Verified locally: removing the directive followed by `go mod tidy` keeps `distribution/reference` at v0.6.0, drops `docker/distribution` from the graph entirely, and `go build ./...` / `go vet ./...` both pass.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/858
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-04-27 15:14:36 +00:00
Renovate Bot
589db33e70 fix(deps): update module github.com/docker/cli to v25.0.7+incompatible (#855)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [github.com/docker/cli](https://github.com/docker/cli) | `v25.0.3+incompatible` → `v25.0.7+incompatible` | ![age](https://developer.mend.io/api/mc/badges/age/go/github.com%2fdocker%2fcli/v25.0.7+incompatible?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/github.com%2fdocker%2fcli/v25.0.3+incompatible/v25.0.7+incompatible?slim=true) |

---

### Release Notes

<details>
<summary>docker/cli (github.com/docker/cli)</summary>

### [`v25.0.7+incompatible`](https://github.com/docker/cli/compare/v25.0.6...v25.0.7)

[Compare Source](https://github.com/docker/cli/compare/v25.0.6...v25.0.7)

### [`v25.0.6+incompatible`](https://github.com/docker/cli/compare/v25.0.5...v25.0.6)

[Compare Source](https://github.com/docker/cli/compare/v25.0.5...v25.0.6)

### [`v25.0.5+incompatible`](https://github.com/docker/cli/compare/v25.0.4...v25.0.5)

[Compare Source](https://github.com/docker/cli/compare/v25.0.4...v25.0.5)

### [`v25.0.4+incompatible`](https://github.com/docker/cli/compare/v25.0.3...v25.0.4)

[Compare Source](https://github.com/docker/cli/compare/v25.0.3...v25.0.4)

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuNSIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS41IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/855
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-04-27 01:01:35 +00:00
Renovate Bot
1032f857a1 fix(deps): update module connectrpc.com/connect to v1.19.2 (#854)
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [connectrpc.com/connect](https://github.com/connectrpc/connect-go) | `v1.19.1` → `v1.19.2` | ![age](https://developer.mend.io/api/mc/badges/age/go/connectrpc.com%2fconnect/v1.19.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/go/connectrpc.com%2fconnect/v1.19.1/v1.19.2?slim=true) |

---

### Release Notes

<details>
<summary>connectrpc/connect-go (connectrpc.com/connect)</summary>

### [`v1.19.2`](https://github.com/connectrpc/connect-go/releases/tag/v1.19.2)

[Compare Source](https://github.com/connectrpc/connect-go/compare/v1.19.1...v1.19.2)

#### What's Changed

##### Governance

- Add [@&#8203;timostamm](https://github.com/timostamm) as a maintainer in [#&#8203;905](https://github.com/connectrpc/connect-go/pull/905) 🎉

##### Bugfixes

- Use 'deadline\_exceeded' instead of 'canceled' on HTTP/2 cancelation when appropriate by [@&#8203;jhump](https://github.com/jhump) in [#&#8203;904](https://github.com/connectrpc/connect-go/pull/904)
- Fix nil pointer deref in duplexHTTPCall under concurrent Send + CloseAndReceive by [@&#8203;simonferquel](https://github.com/simonferquel) in [#&#8203;919](https://github.com/connectrpc/connect-go/pull/919)

##### Other changes

- Refactor memhttptest to work with Go 1.25 synctest by [@&#8203;codefromthecrypt](https://github.com/codefromthecrypt) in [#&#8203;881](https://github.com/connectrpc/connect-go/pull/881)
- Doc clarifications by [@&#8203;emcfarlane](https://github.com/emcfarlane) ([#&#8203;911](https://github.com/connectrpc/connect-go/issues/911), [#&#8203;912](https://github.com/connectrpc/connect-go/issues/912)) and [@&#8203;stefanvanburen](https://github.com/stefanvanburen) ([#&#8203;906](https://github.com/connectrpc/connect-go/issues/906))

#### New Contributors

- [@&#8203;codefromthecrypt](https://github.com/codefromthecrypt) made their first contribution in [#&#8203;881](https://github.com/connectrpc/connect-go/pull/881)
- [@&#8203;simonferquel](https://github.com/simonferquel) made their first contribution in [#&#8203;919](https://github.com/connectrpc/connect-go/pull/919)

**Full Changelog**:

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4xNDEuNSIsInVwZGF0ZWRJblZlciI6IjQzLjE0MS41IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/854
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2026-04-27 01:01:16 +00:00
silverwind
e56b984c04 fix: wait for docker supervise dir before s6-svwait (#851)
`s6-svscan` starts services in parallel, so `act_runner/run` could invoke `s6-svwait` before s6 had created the docker service's `supervise/` directory, failing with `s6-svwait: fatal: unable to s6_svstatus_read: No such file or directory`. Poll for the directory before waiting.

Fixes #760

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/851
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-04-26 22:50:48 +00:00
silverwind
fa5334eb24 fix: Heartbeat ReportState for long-running silent jobs (#852)
Fixes #826.

Regressed in f2d54556 (#819, "perf: reduce runner-to-server connection load with adaptive reporting and polling"). That change added an early-return in `ReportState` whenever there was no state change and no pending outputs, so jobs that produce no log output and no step transitions for many minutes (e.g. a Linux kernel build) stop heartbeating. The server eventually marks the task as orphaned and cancels it while the runner is still executing.

The fix tracks the last successful `UpdateTask` time in an atomic and keeps the no-op skip only while the previous report is younger than `stateReportInterval`. The periodic state ticker fires at exactly `stateReportInterval`, so silent jobs now heartbeat each tick; redundant sends from a `stateNotify` firing right after a tick are still suppressed, preserving the perf intent of #819.

Test added: `TestReporter_StateHeartbeat` asserts the skip path within the interval and the heartbeat path after the interval elapses.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/runner/pulls/852
Reviewed-by: Nicolas <bircni@icloud.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: ChristopherHX <38043+christopherhx@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-04-26 22:49:39 +00:00
Michael Hoang
7c6f1261d4 fix: fetch when other refs get force-pushed (#846)
Due to `NewGitCloneExecutor` fetching all the refs (rather than `GoGitActionCache`), if any refs move in a non-fast-forward fashion, this causes the entire action update to fail. As GitHub has special refs like `pull/N/merge` which are guaranteed to move in a non-fast-forward fashion, this leads actions from GitHub usually failing to update.

```
  ☁  git clone 'https://github.com/Mic92/update-flake-inputs-gitea' # ref=main
  cloning https://github.com/Mic92/update-flake-inputs-gitea to /var/lib/gitea-runner/nix0/.cache/act/9b0155f2957ac84c749f9ecc8afaec823af5ef2e67a104ac655623aee12ca5b2
Non-terminating error while running 'git clone': some refs were not updated
```

With the repo https://github.com/Mic92/update-flake-inputs-gitea, you can notice that it only has a `main` branch that moves in a fast-forward fashion and no tags that could've been force pushed.

Fixes #726

---------

Co-authored-by: Michael Hoang <enzime@users.noreply.github.com>
Reviewed-on: https://gitea.com/gitea/runner/pulls/846
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: Michael Hoang <194829+enzime@noreply.gitea.com>
Co-committed-by: Michael Hoang <194829+enzime@noreply.gitea.com>
2026-04-26 11:08:23 +00:00
silverwind
fbd6316928 Merge pull request 'Align root files with gitea' (#844) from silverwind/act_runner:align-makefile-gitea into main
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/844
Reviewed-by: Bo-Yi Wu (吳柏毅) <appleboy.tw@gmail.com>
2026-04-24 12:59:45 +00:00
silverwind
ade5b8202e Merge branch 'main' into align-makefile-gitea 2026-04-24 12:23:17 +00:00
silverwind
a31f3962c0 Add .dockerignore and align .editorconfig
Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
2026-04-24 13:08:16 +02:00
silverwind
04244fc3f7 Add AGENTS.md and CLAUDE.md
Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
2026-04-24 13:04:47 +02:00
silverwind
cb58492678 Merge pull request 'upgrade go git and yaml' (#842) from lunny/upgrade_dep into main
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/842
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
2026-04-24 11:03:23 +00:00
silverwind
9faadad0ce Add help target and target descriptions
Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
2026-04-24 13:03:23 +02:00
silverwind
352096c5bf Align Makefile with gitea
- Fix `GXZ_PAGAGE` typo to `GXZ_PACKAGE`.
- Move `gxz`/`xgo` tool installs from `deps-backend` to `deps-tools`
  and add `golangci-lint` there; `deps-backend` is just `go mod download`.
- Use `--color=always` + `printf` in `tidy-check` to match `fmt-check`.
- Use STATIC-gated `EXTLDFLAGS` instead of a uname-based toggle, and
  move `-s -w` out of `EXTLDFLAGS` to match gitea's `-ldflags` layout.
- Pass `-s -w -linkmode external -extldflags "-static"` explicitly for
  release-linux / release-windows; add `-s -w` to release-darwin.

Co-Authored-By: Claude (Opus 4.7) <noreply@anthropic.com>
2026-04-24 13:00:37 +02:00
Lunny Xiao
b5c50bb3ab upgrade go git 2026-04-23 14:38:25 -07:00
silverwind
8af9a2b47a Merge gitea/act into act/ folder' (#827)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/827
Reviewed-by: ChristopherHX <38043+christopherhx@noreply.gitea.com>
2026-04-23 16:41:15 +00:00
silverwind
fab2d6ae04 Merge gitea/act into act/
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>
2026-04-22 22:29:06 +02:00
Pascal Zimmermann
15dd63a839 feat: Add support for Dynamic Matrix Strategies with Job Outputs (#149)
# Matrix Strategy: Support for Scalar Values and Template Expressions

## 🎯 Overview

This Pull Request implements support for **scalar values and template expressions** in matrix strategies. Previously, every matrix value had to be declared as a YAML array — a bare scalar like `os: ubuntu-latest` or `version: ${{ fromJSON(...) }}` caused silent failures. Now, scalars are automatically wrapped into single-element arrays, enabling the `${{ fromJSON(...) }}` pattern that is commonly used in GitHub Actions workflows for dynamic matrix generation.

## ⚠️ Semantic Changes

Two subtle behavior changes are introduced that are worth understanding:

### 1. Scalar matrix values now produce a 1-element matrix expansion

**Before this PR:** A scalar value like

```yaml
strategy:
  matrix:
    os: ubuntu-latest   # no brackets
```

failed to decode as `map[string][]interface{}`, so `Matrix()` returned `nil`. The job ran **once, without any matrix context** (`matrix.os` was undefined).

**After this PR:** The same input is wrapped to `["ubuntu-latest"]`. The job runs **one matrix iteration** with `matrix.os = "ubuntu-latest"` set.

> **Impact on existing workflows:** Workflows that accidentally used bare scalars instead of arrays will now run with a matrix context where they previously did not. The functional result is usually the same, but `matrix.*` variables are now populated. This is considered an improvement, but is a semantic change.

---

### 2. Unevaluated template expressions fall back to a literal string value

The actual resolution of `${{ fromJSON(needs.job1.outputs.versions) }}` into an array happens **before** `Matrix()` is called — in `EvaluateYamlNode` inside `pkg/runner/runner.go`. This PR does not change that evaluation path.

If evaluation succeeds → the YAML node already contains a proper array → `Matrix()` decodes it normally.

If evaluation **fails or is skipped** (e.g., expression not yet resolved) → the scalar string is now wrapped as `["${{ fromJSON(...) }}"]` → the job runs **one iteration with the literal unexpanded string** as the matrix value.

> **Note:** The test `TestJobMatrix/matrix_with_template_expression` covers this fallback path, not the successful resolution path. The literal-wrap behavior is the fallback, not the primary mechanism for template expression support.

---

## 🔑 Key Changes

### 1. Flexible Matrix Decoding (`pkg/model/workflow.go`)

#### New Helper Function: `normalizeMatrixValue`
```go
func normalizeMatrixValue(key string, val interface{}) ([]interface{}, error)
```
- Converts a single matrix value to the expected array format
- Handles three cases:
  1. **Arrays**: Pass through unchanged
  2. **Scalars**: Wrap in single-element array (including template expressions)
  3. **Invalid types** (maps, etc.): Return error to detect misconfigurations early

**Supported scalar types:**
- `string` — including unevaluated template expressions
- `int`, `float64` — numeric values
- `bool` — boolean values
- `nil` — null values

#### Enhanced `Matrix()` Method

**Before:**
```go
func (j *Job) Matrix() map[string][]interface{} {
    if j.Strategy.RawMatrix.Kind == yaml.MappingNode {
        var val map[string][]interface{}
        if !decodeNode(j.Strategy.RawMatrix, &val) {
            return nil
        }
        return val
    }
    return nil
}
```

**After:**
```go
func (j *Job) Matrix() map[string][]interface{} {
    if j.Strategy == nil || j.Strategy.RawMatrix.Kind != yaml.MappingNode {
        return nil
    }

    // First decode to flexible map[string]interface{} to handle scalars and arrays
    var flexVal map[string]interface{}
    err := j.Strategy.RawMatrix.Decode(&flexVal)
    if err != nil {
        // If that fails, try the strict array format
        var val map[string][]interface{}
        if !decodeNode(j.Strategy.RawMatrix, &val) {
            return nil
        }
        return val
    }

    // Convert flexible format to expected format with validation
    val := make(map[string][]interface{})
    for k, v := range flexVal {
        normalized, err := normalizeMatrixValue(k, v)
        if err != nil {
            log.Errorf("matrix validation error: %v", err)
            return nil
        }
        val[k] = normalized
    }
    return val
}
```

**Key improvements:**
- Handles unevaluated template expressions as scalar strings
- Automatic conversion to array format for consistent processing
- Validation to catch nested maps and invalid configurations
- Fallback to strict array format decoding for backward compatibility
- Early nil check for strategy to prevent nil pointer panics

---

## 🎁 Use Cases

### Example 1: Template Expression in Matrix (primary motivation)
```yaml
jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      versions: ${{ steps.version-map.outputs.versions }}
    steps:
      - id: version-map
        run: echo "versions=[\"1.17\",\"1.18\",\"1.19\"]" >> $GITHUB_OUTPUT

  test:
    needs: setup
    strategy:
      matrix:
        # EvaluateYamlNode resolves this to an array before Matrix() is called
        version: ${{ fromJSON(needs.setup.outputs.versions) }}
    runs-on: ubuntu-latest
    steps:
      - run: echo "Testing version ${{ matrix.version }}"
```

### Example 2: Mixed Scalar and Array Values
```yaml
jobs:
  build:
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest]  # Array — unchanged
        go-version: 1.19                      # Scalar — wrapped to [1.19]
        node: [14, 16, 18]                    # Array — unchanged
    runs-on: ${{ matrix.os }}
```

### Example 3: Single Value Matrix
```yaml
jobs:
  deploy:
    strategy:
      matrix:
        environment: production  # Scalar — wrapped to ["production"]
    runs-on: ubuntu-latest
```

---

## ✅ Benefits

- ✨ **Feature Parity with GitHub Actions**: Supports template expressions in matrix strategies
- 🔒 **Robust Validation**: Detects misconfigured matrices (nested maps) early
- 🔄 **Backward Compatible**: Existing array-based workflows continue to work
- 📝 **Clear Error Messages**: Helpful validation messages for debugging
- 🛡️ **Type Safety**: Handles all YAML scalar types correctly

---

## 🚀 Breaking Changes

**Technically none in a strictly backward-incompatible sense**, but two semantic changes exist (see [⚠️ Semantic Changes](#️-semantic-changes-read-before-merging) above):

1. Scalar matrix values now iterate (previously they caused a `nil` matrix / no iteration)
2. Unevaluated template strings now produce a literal iteration instead of skipping

Both changes are improvements in practice, but downstream users should be aware.

---

## 📚 Related Work

- Related Gitea Server PR: [go-gitea/gitea#36564](https://github.com/go-gitea/gitea/pull/36564)
- Enables dynamic matrix generation from job outputs
- Supports advanced CI/CD patterns used in GitHub Actions

## 🔗 References

- [GitHub Actions: Matrix strategies](https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs)
- [GitHub Actions: Expression syntax](https://docs.github.com/en/actions/learn-github-actions/expressions)
- [GitHub Actions: `fromJSON` in matrix context](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstrategymatrix)

Reviewed-on: https://gitea.com/gitea/act/pulls/149
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Pascal Zimmermann <pascal.zimmermann@theiotstudio.com>
Co-committed-by: Pascal Zimmermann <pascal.zimmermann@theiotstudio.com>
2026-04-19 22:36:34 +00:00
Bo-Yi Wu
9aafec169b perf: use single poller with semaphore-based capacity control (#822)
## Background

#819 replaced the shared `rate.Limiter` with per-worker exponential backoff counters to add jitter and adaptive polling. Before #819, the poller used:

```go
limiter := rate.NewLimiter(rate.Every(p.cfg.Runner.FetchInterval), 1)
```

This limiter was **shared across all N polling goroutines with burst=1**, effectively serializing their `FetchTask` calls — so even with `capacity=60`, the runner issued roughly one `FetchTask` per `FetchInterval` total.

#819 replaced this with independent per-worker `consecutiveEmpty` / `consecutiveErrors` counters. Each goroutine now backs off **independently**, which inadvertently removed the cross-worker serialization. With `capacity=N`, the runner now has N goroutines each polling on their own schedule — a regression from the pre-#819 baseline for any runner with `capacity > 1`.

(Thanks to @ChristopherHX for catching this in review.)

## Problem

With the post-#819 code:

- `capacity=N` maintains **N persistent polling goroutines**, each calling `FetchTask` independently
- At idle, N goroutines each wake up and send a `FetchTask` RPC per `FetchInterval`
- At full load, N goroutines **continue polling** even though no slot is available to run a new task — every one of those RPCs is wasted
- The `Shutdown()` timeout branch has a pre-existing bug: the "non-blocking check" is actually a blocking receive, so `shutdownJobs()` is never reached on timeout

## Real-World Impact: 3 Runners × capacity=60

Current production environment: 3 runners each with `capacity=60`.

| Metric | Post-#819 (current) | This PR | Reduction |
|--------|---------------------|---------|-----------|
| Polling goroutines (total) | 3 × 60 = **180** | 3 × 1 = **3** | **98.3%** (177 fewer) |
| FetchTask RPCs per poll cycle (idle) | **180** | **3** | **98.3%** |
| FetchTask RPCs per poll cycle (full load) | **180** (all wasted) | **0** (blocked on semaphore) | **100%** |
| Concurrent connections to Gitea | **180** | **3** | **98.3%** |
| Backoff state objects | 180 (per-worker) | 3 (one per runner) | Simplified |

### Idle scenario

All 180 goroutines wake up every `FetchInterval`, each sending a `FetchTask` RPC that returns empty. Server handles 180 RPCs per cycle for zero useful work. After this PR: **3 RPCs per cycle** — one per runner.

> Note: pre-#819 idle behavior was already ~3 RPCs/cycle due to the shared `rate.Limiter`. This PR restores that property while also addressing the full-load case below.

### Full-load scenario (all 180 slots occupied)

All 180 goroutines **continue polling** even though no slot is available. Every RPC is wasted. After this PR: all 3 pollers are **blocked on the semaphore** — **zero RPCs** until a task completes.

> This is a scenario neither the pre-#819 shared limiter nor the post-#819 per-worker backoff handles — both still issue `FetchTask` RPCs when no slot is free. The semaphore is the only approach of the three that ties polling to available capacity.

## Why Not Just Revert to `rate.Limiter`?

Reverting would restore the serialized behavior but is not the right long-term fix:

- **`rate.Limiter` has no concept of available capacity.** At full load it still hands out tokens and issues `FetchTask` RPCs that can't be acted on. The semaphore blocks polling entirely in that case — zero wasted RPCs.
- **It composes poorly with adaptive backoff from #819.** A shared limiter and per-worker backoff pull in different directions.
- **N goroutines serializing on a shared limiter means N-1 of them exist only to wait in line.** A single poller expresses the same behavior more directly.

The semaphore approach ties polling to capacity explicitly: `acquire slot → fetch → dispatch → release`. That invariant becomes structural rather than emergent from a rate limiter.

## Solution

Replace N polling goroutines with a **single polling loop** that uses a buffered channel as a semaphore to control concurrent task execution:

```go
// New: poller.go Poll()
sem := make(chan struct{}, p.cfg.Runner.Capacity)
for {
    select {
    case sem <- struct{}{}:       // Acquire slot (blocks at capacity)
    case <-p.pollingCtx.Done():
        return
    }
    task, ok := p.fetchTask(...)  // Single FetchTask RPC
    if !ok {
        <-sem                     // Release slot on empty response
        // backoff...
        continue
    }
    go func(t *runnerv1.Task) {   // Dispatch task
        defer func() { <-sem }()  // Release slot when done
        p.runTaskWithRecover(p.jobsCtx, t)
    }(task)
}
```

The exponential backoff and jitter from #819 are preserved — just driven by a single `workerState` instead of N per-worker states.

## Shutdown Bug Fix

Fixed a pre-existing bug in `Shutdown()` where the timeout branch could never force-cancel running jobs:

```go
// Before (BROKEN): blocking receive, shutdownJobs() never reached
_, ok := <-p.done   // blocks until p.done is closed
if !ok { return nil }
p.shutdownJobs()    // dead code when jobs are still running

// After (FIXED): proper non-blocking check
select {
case <-p.done:
    return nil
default:
}
p.shutdownJobs()    // now correctly reached on timeout
```

## Code Changes

| Area | Detail |
|------|--------|
| `Poller.runner` | `*run.Runner` → `TaskRunner` interface (enables mock-based testing) |
| `Poll()` | N goroutines → single loop with buffered-channel semaphore |
| `PollOnce()` | Inlined from removed `pollOnce()` |
| `waitBackoff()` | New helper, eliminates duplicated backoff logic |
| `resetBackoff()` | New method on `workerState`, also resets stale `lastBackoff` metric |
| `Shutdown()` | Fixed blocking receive → proper non-blocking select |
| Removed | `poll()`, `pollOnce()` private methods (-2 methods, -42 lines) |

## Test Coverage

Added `TestPoller_ConcurrencyLimitedByCapacity` which verifies:

- With `capacity=3`, at most 3 tasks execute concurrently (`maxConcurrent <= 3`)
- Tasks actually overlap in execution (`maxConcurrent >= 2`)
- `FetchTask` is never called concurrently — confirms single poller (`maxFetchConcur == 1`)
- All 6 tasks complete successfully (`totalCompleted == 6`)
- Mock runner respects context cancellation, enabling shutdown path verification

```
=== RUN   TestPoller_ConcurrencyLimitedByCapacity
--- PASS: TestPoller_ConcurrencyLimitedByCapacity (0.10s)
PASS
ok  	gitea.com/gitea/act_runner/internal/app/poll	0.59s
```

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/822
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2026-04-19 08:10:23 +00:00
silverwind
f923badec7 Use golangci-lint fmt to format code (#163)
Use `golangci-lint fmt` to format code, upgrading `.golangci.yml` to v2 and mirroring the linter configuration used by https://github.com/go-gitea/gitea. `gci` now handles import ordering into standard, project-local, blank, and default groups.

Mirrors https://github.com/go-gitea/gitea/pull/37194.

Changes:
- Upgrade `.golangci.yml` to v2 format with the same linter set as gitea (minus `prealloc`, `unparam`, `testifylint`, `nilnil` which produced too many pre-existing issues)
- Add path-based exclusions (`bodyclose`, `gosec` in tests; `gosec:G115`/`G117` globally)
- Run lint via `make lint-go` in CI instead of `golangci/golangci-lint-action`, matching the pattern used by other Gitea repos
- Apply safe auto-fixes (`modernize`, `perfsprint`, `usetesting`, etc.)
- Add explanations to existing `//nolint` directives
- Remove dead code (unused `newRemoteReusableWorkflow` and `networkName`), duplicate imports, and shadowed `max` builtins
- Replace deprecated `docker/distribution/reference` with `distribution/reference`
- Fix `Deprecated:` comment casing and simplify nil/len checks

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/act/pulls/163
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-04-18 09:10:09 +00:00
silverwind
48944e136c Use golangci-lint fmt to format code (#823)
Use `golangci-lint fmt` to format code, replacing the previous gofumpt-based formatter. https://github.com/daixiang0/gci is used to order the imports.

Also drops the `gitea-vet` dependency since `gci` now handles import ordering.

Mirrors https://github.com/go-gitea/gitea/pull/37194.

---
This PR was written with the help of Claude Opus 4.7

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/823
Reviewed-by: Nicolas <173651+bircni@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-04-17 22:05:01 +00:00
Bo-Yi Wu
40dcee0991 chore(deps): upgrade golangci-lint from v2.10.1 to v2.11.4 (#821)
## Summary
- Bump golangci-lint from v2.10.1 to v2.11.4
- Remove unused `//nolint:revive` directive on metrics package declaration (detected by stricter nolintlint in new version)

## Changes between v2.10.1 and v2.11.4
- **v2.11.0** — Multiple linter dependency upgrades, Go 1.26 support
- **v2.11.2** — Bug fix for `fmt` with path
- **v2.11.3** — gosec update
- **v2.11.4** — Dependency updates (sqlclosecheck, noctx, etc.)

No breaking changes.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/821
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2026-04-15 03:56:34 +00:00
Bo-Yi Wu
f33e5a6245 feat: add Prometheus metrics endpoint for runner observability (#820)
## What

Add an optional Prometheus `/metrics` HTTP endpoint to `act_runner` so operators can observe runner health, polling behavior, job outcomes, and RPC latency without scraping logs.

New surface:

- `internal/pkg/metrics/metrics.go` — metric definitions, custom `Registry`, static Go/process collectors, label constants, `ResultToStatusLabel` helper.
- `internal/pkg/metrics/server.go` — hardened `http.Server` serving `/metrics` and `/healthz` with Slowloris-safe timeouts (`ReadHeaderTimeout` 5s, `ReadTimeout`/`WriteTimeout` 10s, `IdleTimeout` 60s) and a 5s graceful shutdown.
- `daemon.go` wires it up behind `cfg.Metrics.Enabled` (disabled by default).
- `poller.go` / `reporter.go` / `runner.go` instrument their existing hot paths with counters/histograms/gauges — no behavior change.

Metrics exported (namespace `act_runner_`):

| Subsystem | Metric | Type | Labels |
|---|---|---|---|
| — | `info` | Gauge | `version`, `name` |
| — | `capacity`, `uptime_seconds` | Gauge | — |
| `poll` | `fetch_total`, `client_errors_total` | Counter | `result` / `method` |
| `poll` | `fetch_duration_seconds`, `backoff_seconds` | Histogram / Gauge | — |
| `job` | `total` | Counter | `status` |
| `job` | `duration_seconds`, `running`, `capacity_utilization_ratio` | Histogram / GaugeFunc | — |
| `report` | `log_total`, `state_total` | Counter | `result` |
| `report` | `log_duration_seconds`, `state_duration_seconds` | Histogram | — |
| `report` | `log_buffer_rows` | Gauge | — |
| — | `go_*`, `process_*` | standard collectors | — |

All label values are predefined constants — **no high-cardinality labels** (no task IDs, repo URLs, branches, tokens, or secrets) so scraping is safe and bounded.

## Why

Teams self-hosting Gitea + `act_runner` at scale need to answer basic SRE questions that are currently invisible:

- How often are RPCs failing? Which RPC? (`act_runner_client_errors_total`)
- Are runners saturated? (`act_runner_job_capacity_utilization_ratio`, `act_runner_job_running`)
- How long do jobs take? (`act_runner_job_duration_seconds`)
- Is polling backing off? (`act_runner_poll_backoff_seconds`, `act_runner_poll_fetch_total{result=\"error\"}`)
- Are log/state reports slow? (`act_runner_report_{log,state}_duration_seconds`)
- Is the log buffer draining? (`act_runner_report_log_buffer_rows`)

Today operators have to grep logs. This PR makes all of the above first-class metrics so they can feed dashboards and alerts (`rate(act_runner_client_errors_total[5m]) > 0.1`, capacity saturation alerts, etc.).

The endpoint is **disabled by default** and binds to `127.0.0.1:9101` when enabled, so it's opt-in and safe for existing deployments.

## How

### Config

```yaml
metrics:
  enabled: false           # opt-in
  addr: 127.0.0.1:9101     # change to 0.0.0.0:9101 only behind a reverse proxy
```

`config.example.yaml` documents both fields plus a security note about binding externally without auth.

### Wiring

1. `daemon.go` calls `metrics.Init()` (guarded by `sync.Once`), sets `act_runner_info`, `act_runner_capacity`, registers uptime + running-jobs GaugeFuncs, then starts the server goroutine with the daemon context — it shuts down cleanly on `ctx.Done()`.
2. `poller.fetchTask` observes RPC latency / result / error counters. `DeadlineExceeded` (long-poll idle) is treated as an empty result and **not** observed into the histogram so the 5s timeout doesn't swamp the buckets.
3. `poller.pollOnce` reports `poll_backoff_seconds` using the pre-jitter base interval (the true backoff level), and only when it changes — prevents noisy no-op gauge updates at the `FetchIntervalMax` plateau.
4. `reporter.ReportLog` / `ReportState` record duration histograms and success/error counters; `log_buffer_rows` is updated only when the value changes, guarded by the already-held `clientM`.
5. `runner.Run` observes `job_duration_seconds` and increments `job_total` by outcome via `metrics.ResultToStatusLabel`.

### Safety / security review

- All timeouts set; Slowloris-safe.
- Custom `prometheus.NewRegistry()` — no global registration side-effects.
- No sensitive data in labels (reviewed every instrumentation site).
- Single new dependency: `github.com/prometheus/client_golang v1.23.2`.
- Endpoint is unauthenticated by design and documented as such; default localhost bind mitigates exposure. Operators exposing externally should front it with a reverse proxy.

## Verification

### Unit tests

\`\`\`bash
go build ./...
go vet ./...
go test ./...
\`\`\`

### Manual smoke test

1. Enable metrics in `config.yaml`:
   \`\`\`yaml
   metrics:
     enabled: true
     addr: 127.0.0.1:9101
   \`\`\`
2. Start the runner against a Gitea instance: \`./act_runner daemon\`.
3. Scrape the endpoint:
   \`\`\`bash
   curl -s http://127.0.0.1:9101/metrics | grep '^act_runner_'
   curl -s http://127.0.0.1:9101/healthz   # → ok
   \`\`\`
4. Confirm the static series appear immediately: \`act_runner_info\`, \`act_runner_capacity\`, \`act_runner_uptime_seconds\`, \`act_runner_job_running\`, \`act_runner_job_capacity_utilization_ratio\`.
5. Trigger a workflow and confirm counters increment: \`act_runner_poll_fetch_total{result=\"task\"}\`, \`act_runner_job_total{status=\"success\"}\`, \`act_runner_report_log_total{result=\"success\"}\`.
6. Leave the runner idle and confirm \`act_runner_poll_backoff_seconds\` settles (and does **not** churn on every poll).
7. Ctrl-C and confirm a clean \"metrics server shutdown\" log line (no port-in-use error on restart within 5s).

### Prometheus integration

Add to \`prometheus.yml\`:

\`\`\`yaml
scrape_configs:
  - job_name: act_runner
    static_configs:
      - targets: ['127.0.0.1:9101']
\`\`\`

Sample alert to try:

\`\`\`
sum(rate(act_runner_client_errors_total[5m])) by (method) > 0.1
\`\`\`

## Out of scope (follow-ups)

- TLS and auth on the metrics endpoint (mitigated today by localhost default; add when operators need external scraping).
- Per-task labels (intentionally avoided for cardinality safety).

---

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/820
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2026-04-15 01:27:34 +00:00
Bo-Yi Wu
f2d545565f perf: reduce runner-to-server connection load with adaptive reporting and polling (#819)
## Summary

Many teams self-host Gitea + Act Runner at scale. The current runner design causes excessive HTTP requests to the Gitea server, leading to high server load. This PR addresses three root causes: aggressive fixed-interval polling, per-task status reporting every 1 second regardless of activity, and unoptimized HTTP client configuration.

## Problem

The original architecture has these issues:

**1. Fixed 1-second reporting interval (RunDaemon)**

- Every running task calls ReportLog + ReportState every 1 second (2 HTTP requests/sec/task)
- These requests are sent even when there are no new log rows or state changes
- With 200 runners × 3 tasks each = **1,200 req/sec just for status reporting**

**2. Fixed 2-second polling interval (no backoff)**

- Idle runners poll FetchTask every 2 seconds forever, even when no jobs are queued
- No exponential backoff or jitter — all runners can synchronize after network recovery (thundering herd)
- 200 idle runners = **100 req/sec doing nothing useful**

**3. HTTP client not tuned**

- Uses http.DefaultClient with MaxIdleConnsPerHost=2, causing frequent TCP/TLS reconnects
- Creates two separate http.Client instances (one for Ping, one for Runner service) instead of sharing

**Total: ~1,300 req/sec for 200 runners with 3 tasks each**

## Solution

### Adaptive Event-Driven Log Reporting

Replace the recursive `time.AfterFunc(1s)` pattern in RunDaemon with a goroutine-based select event loop using three trigger mechanisms:

| Trigger | Default | Purpose |
|---------|---------|---------|
| `log_report_max_latency` | 3s | Guarantee even a single log line is delivered within this time |
| `log_report_interval` | 5s | Periodic sweep — steady-state cadence |
| `log_report_batch_size` | 100 rows | Immediate flush during bursty output (e.g., npm install) |

**Key design**: `log_report_max_latency` (3s) must be less than `log_report_interval` (5s) so the max-latency timer fires before the periodic ticker for single-line scenarios.

State reporting is decoupled to its own `state_report_interval` (default 5s), with immediate flush on step transitions (start/stop) via a stateNotify channel for responsive frontend UX.

Additionally:
- Skip ReportLog when `len(rows) == 0` (no pending log rows)
- Skip ReportState when `stateChanged == false && len(outputs) == 0` (nothing changed)
- Move expensive `proto.Clone` after the early-return check to avoid deep copies on no-op paths

### Polling Backoff with Jitter

Replace fixed `rate.Limiter` with adaptive exponential backoff:
- Track `consecutiveEmpty` and `consecutiveErrors` counters
- Interval doubles with each empty/error response: `base × 2^(n-1)`, capped at `fetch_interval_max` (default 60s)
- Add ±20% random jitter to prevent thundering herd
- Fetch first, sleep after ��� preserves burst=1 behavior for immediate first fetch on startup and after task completion

### HTTP Client Tuning

- Configure custom `http.Transport` with `MaxIdleConnsPerHost=10` (was 2)
- Share a single `http.Client` between PingService and RunnerService
- Add `IdleConnTimeout=90s` for clean connection lifecycle

## Load Reduction

For 200 runners × 3 tasks (70% with active log output):

| Component | Before | After | Reduction |
|-----------|--------|-------|-----------|
| Polling (idle) | 100 req/s | ~3.4 req/s | 97% |
| Log reporting | 420 req/s | ~84 req/s | 80% |
| State reporting | 126 req/s | ~25 req/s | 80% |
| **Total** | **~1,300 req/s** | **~113 req/s** | **~91%** |

## Frontend UX Impact

| Scenario | Before | After | Notes |
|----------|--------|-------|-------|
| Continuous output (npm install) | ~1s | ~5s | Periodic ticker sweep |
| Single line then silence | ~1s | ≤3s | maxLatencyTimer guarantee |
| Bursty output (100+ lines) | ~1s | <1s | Batch size immediate flush |
| Step start/stop | ~1s | <1s | stateNotify immediate flush |
| Job completion | ~1s | ~1s | Close() retry unchanged |

## New Configuration Options

All have safe defaults — existing config files need no changes:

```yaml
runner:
  fetch_interval_max: 60s        # Max backoff interval when idle
  log_report_interval: 5s        # Periodic log flush interval
  log_report_max_latency: 3s     # Max time a log row waits (must be < log_report_interval)
  log_report_batch_size: 100     # Immediate flush threshold
  state_report_interval: 5s      # State flush interval (step transitions are always immediate)
```

Config validation warns on invalid combinations:
- `fetch_interval_max < fetch_interval` → auto-corrected
- `log_report_max_latency >= log_report_interval` → warning (timer would be redundant)

## Test Plan

- [x] `go build ./...` passes
- [x] `go test ./...` passes (all existing + 3 new tests)
- [x] `golangci-lint run` — 0 issues
- [x] TestReporter_MaxLatencyTimer — verifies single log line flushed by maxLatencyTimer before logTicker
- [x] TestReporter_BatchSizeFlush — verifies batch size threshold triggers immediate flush
- [x] TestReporter_StateNotifyFlush — verifies step transition triggers immediate state flush
- [x] TestReporter_EphemeralRunnerDeletion — verifies Close/RunDaemon race safety
- [x] TestReporter_RunDaemonClose_Race — verifies concurrent Close safety

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/819
Reviewed-by: Nicolas <173651+bircni@noreply.gitea.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2026-04-14 11:29:25 +00:00
Lunny Xiao
90c1275f0e Upgrade yaml (#816)
~wait https://gitea.com/gitea/act/pulls/157~

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/816
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
2026-03-28 16:18:47 +00:00
Lunny Xiao
3232358e71 downgrade yaml from rc.4 to rc.3 and upgrade action lint (#158)
Reviewed-on: https://gitea.com/gitea/act/pulls/158
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
2026-03-28 04:13:07 +00:00
Lunny Xiao
2e98baa34a Upgrade yaml (#157)
Reviewed-on: https://gitea.com/gitea/act/pulls/157
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
2026-03-28 03:31:27 +00:00
Zettat123
505907eb2a Add run_attempt to context (#632)
Blocked by https://gitea.com/gitea/act/pulls/126
Fix https://github.com/go-gitea/gitea/issues/33135

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/632
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2026-03-26 20:07:22 +00:00
silverwind
9933ea0d92 feat: add configurable bind_workdir option with workspace cleanup for DinD setups (#810)
## Summary

Adds a `container.bind_workdir` config option that exposes the nektos/act `BindWorkdir` setting. When enabled, workspaces are bind-mounted from the host filesystem instead of Docker volumes, which is required for DinD setups where jobs use `docker compose` with bind mounts (e.g. `.:/app`).

Each job gets an isolated workspace at `/workspace/<task_id>/<owner>/<repo>` to prevent concurrent jobs from the same repo interfering with each other. The task directory is cleaned up after job execution.

### Configuration

```yaml
container:
  bind_workdir: true
```

When using this with DinD, also mount the workspace parent into the runner container and add it to `valid_volumes`:
```yaml
container:
  valid_volumes:
    - /workspace/**
```

*This PR was authored by Claude (AI assistant)*

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/810
Reviewed-by: ChristopherHX <38043+christopherhx@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-03-03 10:15:06 +00:00
RoboMagus
5dd5436169 Semver tags for Docker images (#720)
The main Gitea docker image is already distributed with proper semver tags, such that users can pin to e.g. the minor version and still pull in patch releases.  This is something that has been lacking on the act runner images.

This PR expands the docker image tag versioning strategy such that when `v0.2.13` is released the following image tags are produced:

Basic:
 - `0`
 - `0.2`
 - `0.2.13`
 - `latest`

DinD:
 - `0-dind`
 - `0.2-dind`
 - `0.2.13-dind`
 - `latest-dind`

DinD-Rootless:
 - `0-dind-rootless`
 - `0.2-dind-rootless`
 - `0.2.13-dind-rootless`
 - `latest-dind-rootless`

To verify the `docker/metadata-action` produces the expected results in a Gitea workflow environment I've executed a release workflow. Results can be seen in [this run](https://gitea.com/RoboMagus/gitea_act_runner/actions/runs/14). (Note though that as the repository name of my fork changed from `act_runner` to `gitea_act_runner` this is reflected in the produced docker tags in this test run!)

---------

Co-authored-by: RoboMagus <68224306+RoboMagus@users.noreply.github.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@noreply.gitea.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/720
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: RoboMagus <robomagus@noreply.gitea.com>
Co-committed-by: RoboMagus <robomagus@noreply.gitea.com>
2026-02-25 19:09:53 +00:00
Lunny Xiao
28740d7788 Upgrade go mod (#154)
Reviewed-on: https://gitea.com/gitea/act/pulls/154
Reviewed-by: silverwind <silverwind@noreply.gitea.com>
2026-02-22 20:39:43 +00:00
Lunny Xiao
ddf9159a8f Remove jobparser because of moving to Gitea main project (#155)
Reviewed-on: https://gitea.com/gitea/act/pulls/155
Reviewed-by: silverwind <silverwind@noreply.gitea.com>
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
2026-02-22 19:04:16 +00:00
silverwind
658101d9cb chore(lint): add golangci-lint v2 and fix all lint issues (#803)
## Summary
- Replace old `.golangci.yml` (v1 format) with v2 format, aligned with gitea's lint config
- Add `lint-go`, `lint-go-fix`, and `lint` Makefile targets using golangci-lint v2.10.1
- Replace `make vet` with `make lint` in CI workflow (lint includes vet)
- Fix all 35 lint issues: modernize (maps.Copy, range over int, any), perfsprint (errors.New), unparam (remove unused parameters), revive (var naming), staticcheck, forbidigo exclusion for cmd/
- Make `security-check` non-fatal (apply https://github.com/go-gitea/gitea/pull/36681)
- Remove dead gocritic exclusion rules (commentFormatting, exitAfterDefer)
- Remove dead linter exclusions and disabled checks (singleCaseSwitch, ST1003, QF1001, QF1006, QF1008, testifylint go-require/require-error, test file exclusions for dupl/errcheck/staticcheck/unparam)

## Test plan
- [x] `golangci-lint run` passes
- [x] `go build ./...` passes
- [x] `go test ./...` passes

---------

Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/803
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
2026-02-22 17:35:08 +00:00
ChristopherHX
f0f9f0c8ab fix: composite action log result reported as step result (#801)
Act logs an array of stepID to signal that this is an partial step result within an composite actions, this is the case for years and act_runner never handled it correctly.

Ref: <43e6958fa3/pkg/runner/logger.go (L142)>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/801
Reviewed-by: silverwind <silverwind@noreply.gitea.com>
2026-02-22 14:56:09 +00:00
silverwind
ae6e5dfcf7 fix: race condition in reporter between RunDaemon and Close (#796)
## Summary

- Fix data race on `r.closed` between `RunDaemon()` and `Close()` by protecting it with the existing `stateMu` — `closed` is part of the reporter state. `RunDaemon()` reads it under `stateMu.RLock()`, `Close()` sets it inside the existing `stateMu.Lock()` block
- `ReportState` now has a parameter to not report results from runDaemon even if set, from now on `Close` reports the result
- `Close` waits for `RunDaemon()` to signal exit via a closed channel `daemon` before reporting the final logs and state with result, unless something really wrong happens it does not time out
- Add `TestReporter_EphemeralRunnerDeletion` which reproduces the exact scenario from #793: RunDaemon's `ReportState` racing with `Close`, causing the ephemeral runner to be deleted before final logs are sent
- Add `TestReporter_RunDaemonClose_Race` which exercises `RunDaemon()` and `Close()` concurrently to verify no data race on `r.closed` under `go test -race`
- Enable `-race` flag in `make test` so CI catches data races going forward

Based on #794, with fixes for the remaining unprotected `r.closed` reads that the race detector catches.

Fixes #793

---------

Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: rmawatson <rmawatson@hotmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/796
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-22 14:52:49 +00:00
silverwind
f4418eff18 chore(deps): update go dependencies (#802)
## Summary
- Update all Go dependencies to latest compatible versions
- Notable updates: connectrpc v1.19.1, cobra v1.10.2, protobuf v1.36.11, otel v1.40.0, actionlint v1.7.11
- docker/docker kept at v25 (v28+ has breaking module restructuring)
- Fix govulncheck issues: update go-git v5.16.2 → v5.16.5 (GO-2026-4473), containerd v1.7.13 → v1.7.29 (GO-2025-4108, GO-2025-4100, GO-2025-3528)
- Remove unnecessary go-git replace directive
- Add `make tidy` and `make tidy-check` Makefile targets
- Clarify `distribution/reference` replace directive comment (needed because `docker/distribution@v2.8.3` uses `reference.SplitHostname` removed in v0.6.0)

## Test plan
- [x] `go build ./...` passes
- [x] `go test ./...` passes
- [x] `govulncheck ./...` passes

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/802
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-22 11:59:47 +00:00
silverwind
0f7efae806 chore(deps): update workflow action versions (#792)
## Summary
- `actions/checkout`: v4/v5 → v6
- `actions/setup-go`: v5 → v6
- `docker/build-push-action`: v5 → v6

All other actions (`goreleaser/goreleaser-action@v6`, `docker/setup-qemu-action@v3`, `docker/setup-buildx-action@v3`, `docker/login-action@v3`, `crazy-max/ghaction-import-gpg@v6`) were already at their latest major versions.

No breaking changes affect the current workflow configurations. The main changes in the updated actions are Node.js 24 runtime upgrades and minor feature additions.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/792
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-18 06:05:34 +00:00
silverwind
aab249000c chore(deps): update act to v0.261.8 (#791)
## Summary
- Update `gitea.com/gitea/act` from pseudo-version `v0.261.7-0.20251202193638-5417d3ac6742` to `v0.261.8`
- Update yaml import in `workflow.go` from `gopkg.in/yaml.v3` to `go.yaml.in/yaml/v4` to match act's yaml v4 migration
- Add tests to verify yaml/v4 upgrade works correctly

## Changes included in act v0.261.8
- Recover from panics in parallel executor workers ([gitea/act#153](https://gitea.com/gitea/act/pulls/153))
- Fix max-parallel support for matrix jobs ([gitea/act#150](https://gitea.com/gitea/act/pulls/150))
- Fix yaml with prefixed newline broken setjob + yaml v4 ([gitea/act#144](https://gitea.com/gitea/act/pulls/144))
- Fixed typo ([gitea/act#151](https://gitea.com/gitea/act/pulls/151))

## Tests added
- **`Test_generateWorkflow`**: 7 new cases (was 1), covering: no needs, needs as list, workflow env/triggers, container+services, matrix strategy, special YAML characters, and invalid YAML error handling
- **`Test_yamlV4NodeRoundTrip`**: Directly exercises `go.yaml.in/yaml/v4` — `yaml.Node` construction, marshal/unmarshal round-trip, and node kind constants

Fixes: https://gitea.com/gitea/act_runner/issues/371
Fixes: https://gitea.com/gitea/act_runner/issues/772
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/791
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-18 05:37:21 +00:00
silverwind
43e6958fa3 Recover from panics in parallel executor workers (#153)
The worker goroutines in `NewParallelExecutor` had no panic recovery. A panic in any executor (e.g. expression evaluation) would crash the entire runner process, leaving all steps stuck in the UI because the final status was never reported back.

Add `defer`/`recover` in the worker goroutines to convert panics into errors that propagate through the normal error channel.

Issue is present in latest `nektos/act` as well.

Fixes https://gitea.com/gitea/act_runner/issues/371

(Partially generated by Claude)

Reviewed-on: https://gitea.com/gitea/act/pulls/153
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-18 03:29:07 +00:00
stecklars
c82077e6b5 examples: improve DIND rootless network performance (#786)
## Summary
- Add `DOCKERD_ROOTLESS_ROOTLESSKIT_NET=slirp4netns` and `DOCKERD_ROOTLESS_ROOTLESSKIT_MTU=65520` to the DIND docker-compose example
- The `docker:dind-rootless` base image defaults to vpnkit as the network driver, which has substantially lower throughput than slirp4netns

## The problem

I noticed that pulling containers as well as downloading data within the container when running act_runner as DIND was very slow (see Ookla speedtest results in the following). While analysing the issue, I found that this was caused by the usage of vpnkit.

The `docker:dind-rootless` base image defaults to vpnkit as the network driver. slirp4netns was [added as an opt-in option](https://github.com/docker-library/docker/pull/543) and must be explicitly enabled via `DOCKERD_ROOTLESS_ROOTLESSKIT_NET=slirp4netns`.

This means anyone following the current DIND example gets vpnkit, which has significantly lower network throughput. This affects **all** network operations in the container — image pulls, package installs, and CI tasks.

Per the [rootlesskit iperf3 benchmarks](https://github.com/rootless-containers/rootlesskit/blob/master/docs/network.md):

| Driver | MTU 1500 | MTU 65520 |
|--------|----------|-----------|
| **vpnkit** | 0.60 Gbps | not supported |
| **slirp4netns** | 1.06 Gbps | 7.55 Gbps |

## Real-world benchmark results (Ookla speedtest, same server)

| | Download | Upload |
|---|---|---|
| **Default (vpnkit)** | ~130 Mbps | ~126 Mbps |
| **slirp4netns + MTU 65520** | ~958 Mbps | ~462 Mbps |

## References
- [docker-library/docker#543](https://github.com/docker-library/docker/pull/543) — added slirp4netns to dind-rootless as opt-in (vpnkit remains default)
- [rootlesskit network docs](https://github.com/rootless-containers/rootlesskit/blob/master/docs/network.md) — iperf3 benchmarks

---------

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/786
Reviewed-by: silverwind <silverwind@noreply.gitea.com>
Co-authored-by: stecklars <stecklars@noreply.gitea.com>
Co-committed-by: stecklars <stecklars@noreply.gitea.com>
2026-02-16 07:56:45 +00:00
silverwind
9f91fdd0eb chore(deps): bump Go version to 1.26 in Dockerfile and Makefile (#788)
## Summary
- Bump Dockerfile builder image from `golang:1.24-alpine` to `golang:1.26-alpine`
- Bump Makefile `XGO_VERSION` from `go-1.24.x` to `go-1.26.x`

These were missed in #787 which bumped `go.mod` to Go 1.26.0, causing CI build failures.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/788
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-14 10:32:16 +00:00
silverwind
7a7a5d9051 chore(deps): bump Go version to 1.26.0 (#787)
## Summary
- Bumps Go version from 1.24.0 (toolchain go1.24.11) to 1.26.0
- Fixes CI `govulncheck` failures caused by three standard library vulnerabilities in go1.24.11:
  - GO-2026-4341: Memory exhaustion in `net/url` query parameter parsing
  - GO-2026-4340: Handshake messages at incorrect encryption level in `crypto/tls`
  - GO-2026-4337: Unexpected session resumption in `crypto/tls`

## Test plan
- [x] `make vet` passes
- [x] `make build` passes
- [x] `make test` passes (includes `govulncheck` and all unit tests)

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/787
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2026-02-14 10:19:45 +00:00
Pascal Zimmermann
c0f19d9a26 fix: Max parallel Support for Matrix Jobs and Remote Action Tests (#150)
## Summary

This PR fixes the `max-parallel` strategy configuration for matrix jobs and resolves all failing tests in `step_action_remote_test.go`. The implementation ensures that matrix jobs respect the `max-parallel` setting, preventing resource exhaustion when running GitHub Actions workflows.

## Problem Statement

### Issue 1: max-parallel Not Working Correctly
Matrix jobs were running in parallel regardless of the `max-parallel` setting in the strategy configuration. This caused:
- Resource contention on limited runners
- Unpredictable job execution behavior
- Inability to control concurrency for resource-intensive workflows

### Issue 2: Failing Remote Action Tests
All tests in `step_action_remote_test.go` were failing due to:
- Missing `ActionCacheDir` configuration
- Incorrect mock expectations using fixed strings instead of hash-based paths
- Incompatibility with the hash-based action cache implementation

## Changes

### 1. max-parallel Implementation (`pkg/runner/runner.go`)

#### Robust Initialization
Added fallback logic to ensure `MaxParallel` is always properly initialized:
```go
if job.Strategy.MaxParallel == 0 {
    job.Strategy.MaxParallel = job.Strategy.GetMaxParallel()
}
```

#### Eliminated Unnecessary Nesting
Fixed inefficient nested parallelization when only one pipeline element exists:
```go
if len(pipeline) == 1 {
    // Execute directly without additional wrapper
    log.Debugf("Single pipeline element, executing directly")
    return pipeline[0](ctx)
}
```

#### Enhanced Logging
Added comprehensive debug and info logging:
- Shows which `maxParallel` value is being used
- Logs adjustments based on matrix size
- Reports final parallelization decisions

### 2. Worker Logging (`pkg/common/executor.go`)

Enhanced `NewParallelExecutor` with detailed worker activity logging:
```go
log.Infof("NewParallelExecutor: Creating %d workers for %d executors", parallel, len(executors))

for i := 0; i < parallel; i++ {
    go func(workerID int, work <-chan Executor, errs chan<- error) {
        log.Debugf("Worker %d started", workerID)
        taskCount := 0
        for executor := range work {
            taskCount++
            log.Debugf("Worker %d executing task %d", workerID, taskCount)
            errs <- executor(ctx)
        }
        log.Debugf("Worker %d finished (%d tasks executed)", workerID, taskCount)
    }(i, work, errs)
}
```

**Benefits:**
- Easy verification of correct parallelization
- Better debugging capabilities
- Clear visibility into worker activity

### 3. Documentation (`pkg/model/workflow.go`)

Added clarifying comment to ensure strategy values are always set:
```go
// Always set these values, even if there's an error later
j.Strategy.FailFast = j.Strategy.GetFailFast()
j.Strategy.MaxParallel = j.Strategy.GetMaxParallel()
```

### 4. Test Fixes (`pkg/runner/step_action_remote_test.go`)

#### Added Missing Configuration
All tests now include `ActionCacheDir`:
```go
Config: &Config{
    GitHubInstance: "github.com",
    ActionCacheDir: "/tmp/test-cache",
}
```

#### Hash-Based Suffix Matchers
Updated mocks to use hash-based paths instead of fixed strings:
```go
// Before
sarm.On("readAction", sar.Step, suffixMatcher("org-repo-path@ref"), ...)

// After
sarm.On("readAction", sar.Step, suffixMatcher(sar.Step.UsesHash()), ...)
```

#### Flexible Exec Matcher for Post Tests
Implemented flexible path matching for hash-based action directories:
```go
execMatcher := mock.MatchedBy(func(args []string) bool {
    if len(args) != 2 {
        return false
    }
    return args[0] == "node" && strings.Contains(args[1], "post.js")
})
```

#### Token Test Improvements
- Uses unique cache directory to force cloning
- Validates URL redirection to github.com
- Accepts realistic token behavior

### 5. New Tests

#### Unit Tests (`pkg/runner/max_parallel_test.go`)
Tests various `max-parallel` configurations:
- `max-parallel: 1` → Sequential execution
- `max-parallel: 2` → Max 2 parallel jobs
- `max-parallel: 4` (default) → Max 4 parallel jobs
- `max-parallel: 10` → Max 10 parallel jobs

#### Concurrency Test (`pkg/common/executor_max_parallel_test.go`)
Verifies that `max-parallel: 2` actually limits concurrent execution using atomic counters.

## Expected Behavior

### Before
- ❌ Jobs ran in parallel regardless of `max-parallel` setting
- ❌ Unnecessary nested parallelization (8 workers for 1 element)
- ❌ No logging to debug parallelization issues
- ❌ All `step_action_remote_test.go` tests failing

### After
- ✅ `max-parallel: 1` → Jobs run strictly sequentially
- ✅ `max-parallel: N` → Maximum N jobs run simultaneously
- ✅ Efficient single-level parallelization for matrix jobs
- ✅ Comprehensive logging for debugging
- ✅ All tests passing (10/10)

## Example Log Output

With `max-parallel: 2` and 6 matrix jobs:
```
[DEBUG] Using job.Strategy.MaxParallel: 2
[INFO] Running job with maxParallel=2 for 6 matrix combinations
[DEBUG] Single pipeline element, executing directly
[INFO] NewParallelExecutor: Creating 2 workers for 6 executors
[DEBUG] Worker 0 started
[DEBUG] Worker 1 started
[DEBUG] Worker 0 executing task 1
[DEBUG] Worker 1 executing task 1
...
[DEBUG] Worker 0 finished (3 tasks executed)
[DEBUG] Worker 1 finished (3 tasks executed)
```

## Test Results

All tests pass successfully:
```
✅ TestStepActionRemote (3 sub-tests)
✅ TestStepActionRemotePre
✅ TestStepActionRemotePreThroughAction
✅ TestStepActionRemotePreThroughActionToken
✅ TestStepActionRemotePost (4 sub-tests)
✅ TestMaxParallelStrategy
✅ TestMaxParallel2Quick

Total: 12/12 tests passing
```

## Breaking Changes

None. This PR is fully backward compatible. All changes improve existing behavior without altering the API.

## Impact

- ✅ Fixes resource management for CI/CD pipelines
- ✅ Prevents runner exhaustion on limited infrastructure
- ✅ Enables sequential execution for resource-intensive jobs
- ✅ Improves debugging with detailed logging
- ✅ Ensures test suite reliability

## Files Modified

### Core Implementation
- `pkg/runner/runner.go` - max-parallel fix + logging
- `pkg/common/executor.go` - Worker logging
- `pkg/model/workflow.go` - Documentation

### Tests
- `pkg/runner/step_action_remote_test.go` - Fixed all 10 tests
- `pkg/runner/max_parallel_test.go` - **NEW** - Unit tests
- `pkg/common/executor_max_parallel_test.go` - **NEW** - Concurrency test

## Verification

### Manual Testing
```bash
# Build
go build -o dist/act main.go

# Test with max-parallel: 2
./dist/act -W test-max-parallel-2.yml -v

# Expected: Only 2 jobs run simultaneously
```

### Automated Testing
```bash
# Run all tests
go test ./pkg/runner -run TestStepActionRemote -v
go test ./pkg/runner -run TestMaxParallel -v
go test ./pkg/common -run TestMaxParallel -v
```

## Related Issues

Fixes issues where matrix jobs in Gitea ignored the `max-parallel` strategy setting, causing resource contention and unpredictable behavior.

Reviewed-on: https://gitea.com/gitea/act/pulls/150
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: silverwind <silverwind@noreply.gitea.com>
Co-authored-by: Pascal Zimmermann <pascal.zimmermann@theiotstudio.com>
Co-committed-by: Pascal Zimmermann <pascal.zimmermann@theiotstudio.com>
2026-02-11 00:43:38 +00:00
wrzooz
495185446f Fixed typo (#151)
Reviewed-on: https://gitea.com/gitea/act/pulls/151
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: wrzooz <wrzooz@noreply.gitea.com>
Co-committed-by: wrzooz <wrzooz@noreply.gitea.com>
2026-01-22 08:09:32 +00:00
mkesper
90d11b8692 Remove duplicate assignment of DIST (#777)
The assignment already happens at line 1.

Signed-off-by: mkesper <mkesper@noreply.gitea.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/777
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: mkesper <mkesper@noreply.gitea.com>
Co-committed-by: mkesper <mkesper@noreply.gitea.com>
2025-12-26 00:38:38 +00:00
Christopher Homberger
c4b57fbcb2 chore(deps): upgrade dependencies (#775)
CI uses latest go 24, we may need a cron job after go updates.

Closes https://gitea.com/gitea/act_runner/issues/774

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/775
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2025-12-20 23:35:15 +00:00
Christopher Homberger
3a07d231a0 Fix yaml with prefixed newline broken setjob + yaml v4 (#144)
* go-yaml v3 **and** v4 workaround
* avoid yaml.Marshal broken indention

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/144
Reviewed-by: wxiaoguang <wxiaoguang@noreply.gitea.com>
Reviewed-by: Zettat123 <zettat123@noreply.gitea.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2025-12-09 02:28:56 +00:00
Zettat123
e6dbe2a1ca Bump gitea/act (#770)
Related to https://gitea.com/gitea/act/pulls/145

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/770
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2025-12-02 22:38:31 +00:00
Zettat123
5417d3ac67 Interpolate uses for remote reusable workflows (#145)
Related to #127

Reviewed-on: https://gitea.com/gitea/act/pulls/145
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2025-12-02 19:36:38 +00:00
Max P.
774f316c8b fix(dockerfile): Pin docker dind images to version 28, as version 29 has breaking changes in the API that are currently causing problems with the act fork of Gitea (#769)
The effect probably does not **yet** occur in the published versions because the last publication took place before the release of Docker v29.

Therefore, no one should expect version 29 of Docker to be used, so there are basically no side effects.

---

fix #768

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/769
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: Max P. <mail@0xMax42.io>
Co-committed-by: Max P. <mail@0xMax42.io>
2025-11-25 14:40:27 +00:00
DavidToneian
823e9489d6 Fix some typos in internal/pkg/config/config.example.yaml (#764)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/764
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: DavidToneian <davidtoneian@noreply.gitea.com>
Co-committed-by: DavidToneian <davidtoneian@noreply.gitea.com>
2025-11-19 18:16:45 +00:00
DavidToneian
dc38bf1895 Fix URL to documentation of runner images (#765)
The previous URL, `https://gitea.com/docker.gitea.com/runner-images`, leads to a 404 currently.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/765
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: DavidToneian <davidtoneian@noreply.gitea.com>
Co-committed-by: DavidToneian <davidtoneian@noreply.gitea.com>
2025-11-19 18:16:08 +00:00
Akkuman
96b866a3a8 chore: update config.example.yaml for https://gitea.com/gitea/act_runner/pulls/724 (#763)
for pr https://gitea.com/gitea/act_runner/pulls/724

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/763
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
Co-authored-by: Akkuman <akkumans@qq.com>
Co-committed-by: Akkuman <akkumans@qq.com>
2025-11-18 07:18:38 +00:00
Zettat123
f56fd693ee Add run_attempt to context (#126)
Fix https://github.com/go-gitea/gitea/issues/33135

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/126
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2025-11-14 20:14:23 +00:00
WANG Xuerui
3b11bac2ad ci: release binary for linux/loong64 (#756)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/756
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: WANG Xuerui <git@xen0n.name>
Co-committed-by: WANG Xuerui <git@xen0n.name>
2025-10-21 17:42:08 +00:00
Zettat123
47caafd037 Bump gitea/act (#753)
Related to https://gitea.com/gitea/act/pulls/123

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/753
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2025-10-14 03:32:35 +00:00
Zettat123
34f68b3c18 Support cloning actions and workflows from private repos (#123)
Related to https://github.com/go-gitea/gitea/pull/32562

Resolve https://gitea.com/gitea/act_runner/issues/102

To support using actions and workflows from private repositories, we need to enable act_runner to clone private repositories.
~~But it is not easy to know if a repository is private and whether a token is required when cloning. In this PR, I added a new option `RetryToken`. By default, token is empty. When cloning a repo returns an `authentication required` error, `act_runner` will try to clone the repo again using `RetryToken` as the token.~~

In this PR, I added a new `getGitCloneToken` function. This function returns `GITEA_TOKEN` for cloning remote actions or remote reusable workflows when the cloneURL is from the same Gitea instance that the runner is registered to. Otherwise, it returns an empty string as token for cloning public repos from other instances (such as GitHub).

Thanks @ChristopherHX for https://gitea.com/gitea/act/pulls/123#issuecomment-1046171 and https://gitea.com/gitea/act/pulls/123#issuecomment-1046285.

Reviewed-on: https://gitea.com/gitea/act/pulls/123
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2025-10-14 02:37:09 +00:00
ChristopherHX
50e0509007 Do not implicitly mount /var/run/docker.sock (#751)
* podman creates an folder
* dind sees a folder and fails

Was adding the mount a mistake?, a feature with side effects?

Closes https://gitea.com/gitea/act_runner/issues/750

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/751
Reviewed-by: Jason Song <wolfogre@noreply.gitea.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-committed-by: ChristopherHX <christopher.homberger@web.de>
2025-10-09 07:16:56 +00:00
Zettat123
ac6e4b7517 Support inputs context when parsing jobs (#143)
Reusable workflows or manually triggered workflows may get data from [`inputs` context](https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#inputs-context).

For example:

```
name: Build

run-name: Build app on ${{ inputs.os }}

on:
  workflow_dispatch:
    inputs:
      os:
        description: Select the OS
        required: true
        type: choice
        options:
          - windows
          - linux

...
```

Reviewed-on: https://gitea.com/gitea/act/pulls/143
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2025-10-03 18:05:12 +00:00
Christopher Homberger
8920c4a170 Timeout to wait for and optionally require docker always (#741)
* do not require docker cli in the runner image for waiting for a sidecar dockerd
* allow to specify that `:host` labels would also only be accepted if docker is reachable

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/741
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2025-08-28 17:28:08 +00:00
Akkuman
bbf9d7e90f feat: support github mirror (#716)
ref: https://github.com/go-gitea/gitea/issues/34858

when github_mirror='https://ghfast.top/https://github.com'

it will clone from the github mirror

However, there is an exception: because the cache is hashed using the string, if the same cache has been used before, it will still be pulled from github, only for the newly deployed act_ruuner

In the following two scenarios, it will solve the problem encountered:

1. some github mirror is  https://xxx.com/https://github.com/actions/checkout@v4, it will report error `Expected format {org}/{repo}[/path]@ref. Actual ‘https://xxx.com/https://github.com/actions/checkout@v4’ Input string was not in a correct format`
2. If I use an action that has a dependency on another action, even if I configure the url of the action I want to use, the action that the action introduces will still pull from github.
for example 02882cc2d9/action.yml (L127-L132)

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/716
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Akkuman <akkumans@qq.com>
Co-committed-by: Akkuman <akkumans@qq.com>
2025-08-28 16:57:01 +00:00
Daan Selen
46f471a900 refac(workflow): use matrix to compile different docker images (#740)
I have made this to speed up and make it more robust.

The matrix executes the jobs in parallel, doing some things perhaps double. But making overall management easier due to the simple defined variables at the top of the matrix declaration.

Co-authored-by: Daan Selen <dselen@systemec.nl>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/740
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: Daan Selen <dselen@nerthus.nl>
Co-committed-by: Daan Selen <dselen@nerthus.nl>
2025-08-22 23:49:20 +00:00
DaanSelen
aa28f8d99c feat(docker): add TZ env variable working (#738)
Relevant: https://gitea.com/gitea/act_runner/issues/735

See my example below, `edit` is my PR, `non-edit` is the origin/main.
```
dselen@N-DESKTOP1:~/development/act_runner$ docker images
REPOSITORY   TAG        IMAGE ID       CREATED          SIZE
runner       edit       b12322f8c3f0   26 seconds ago   43.5MB
runner       non-edit   e5593ad32c16   34 minutes ago   43.1MB

dselen@N-DESKTOP1:~/development/act_runner$ docker run -d -e TZ=Europe/Amsterdam runner:non-edit
5f26979515f461a2a7e342aa586d7b91224d2d3c3dcf1ed0c1e7293ff00645a4

dselen@N-DESKTOP1:~/development/act_runner$ docker run -d -e TZ=Europe/Amsterdam runner:edit
9cc5fc6b364cf07776d97c6c60c03f23372eb2c93c7da8d3d80f4f6dc2a6b10e

dselen@N-DESKTOP1:~/development/act_runner$ docker ps
CONTAINER ID   IMAGE             COMMAND                  CREATED         STATUS         PORTS     NAMES
9cc5fc6b364c   runner:edit       "/sbin/tini -- run.sh"   2 seconds ago   Up 2 seconds             serene_bardeen
5f26979515f4   runner:non-edit   "/sbin/tini -- run.sh"   5 seconds ago   Up 5 seconds             jovial_euler

dselen@N-DESKTOP1:~/development/act_runner$ docker exec -it jovial_euler bash
5f26979515f4:/# date
Thu Aug 21 16:40:35 UTC 2025

dselen@N-DESKTOP1:~/development/act_runner$ docker exec -it serene_bardeen bash
9cc5fc6b364c:/# date
Thu Aug 21 18:40:42 CEST 2025
```

I do not see why this would not be acceptable, its only 400KB

Regards.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/738
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
Co-authored-by: DaanSelen <daanselen@noreply.gitea.com>
Co-committed-by: DaanSelen <daanselen@noreply.gitea.com>
2025-08-21 23:56:32 +00:00
Christopher Homberger
6a7e18b124 Allow node24 actions (#737)
* `node` Tool is used regardless of this change
* upgrade images with `node` = `node24` if required
* actions/checkout@v5 now passing validation

Fixes #729

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/737
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2025-08-21 17:47:26 +00:00
Christopher Homberger
91852faf93 refactor: simplify adding new node versions add node 24 (#140)
* backport

Reviewed-on: https://gitea.com/gitea/act/pulls/140
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2025-08-21 16:04:08 +00:00
ChristopherHX
39509e9ad0 Support Simplified Concurrency (#139)
- update RawConcurrency struct to parse and serialize string-based concurrency notation
- update EvaluateConcurrency to handle new RawConcurrency format

Reviewed-on: https://gitea.com/gitea/act/pulls/139
Reviewed-by: Zettat123 <zettat123@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-committed-by: ChristopherHX <christopher.homberger@web.de>
2025-07-29 09:14:47 +00:00
telackey
53329c46ff Add ubuntu-24.04 label to defaults. (#724)
Simple change to include ubuntu-24.04 in the default list.  While ubuntu-latest already points to the same image (at this time) it is appropriate to have it by version as well.

Co-authored-by: techknowlogick <techknowlogick@gitea.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/724
Co-authored-by: telackey <telackey@noreply.gitea.com>
Co-committed-by: telackey <telackey@noreply.gitea.com>
2025-07-22 14:47:30 +00:00
Lunny Xiao
6b1aea9c04 Upgrade github.com/go-git/go-git/v5 to v5.16.2 (#706)
Fix #695

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/706
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
2025-06-11 17:59:35 +00:00
lautriva
edec9a8f91 Add a way to specify vars in act_runner exec (#704)
Before this commit, when running locally `act_runner exec` to test workflows, we could only fill env and secrets but not vars
This commit add a new exec option `--var` based on what is done for env and secret

Example:

`act_runner exec --env MY_ENV=testenv -s MY_SECRET=testsecret --var MY_VAR=testvariable`

 workflow
```
name: Gitea Actions test
on: [push]

jobs:
  TestAction:
    runs-on: ubuntu-latest
    steps:
      - run: echo "VAR -> ${{ vars.MY_VAR }}"
      - run: echo "ENV -> ${{ env.MY_ENV }}"
      - run: echo "SECRET -> ${{ secrets.MY_SECRET }}"
```

Will echo var, env and secret values sent in the command line

Fixes gitea/act_runner#692

Co-authored-by: Lautriva <gitlactr@dbn.re>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/704
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: lautriva <lautriva@noreply.gitea.com>
Co-committed-by: lautriva <lautriva@noreply.gitea.com>
2025-06-11 17:38:29 +00:00
Pablo Carranza
6a9a447f86 Report errors by setting raw_output when it's error level (#645)
This solves #643 by setting the "raw_output" entry attribute when the log level is error.  This results in the log line being shipped to the Gitea UI.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/645
Reviewed-by: Zettat123 <zettat123@noreply.gitea.com>
Co-authored-by: Pablo Carranza <pcarranza@gmail.com>
Co-committed-by: Pablo Carranza <pcarranza@gmail.com>
2025-06-05 17:53:13 +00:00
badhezi
9924aea786 Evaluate run-name field for workflows (#137)
To support https://github.com/go-gitea/gitea/pull/34301

Reviewed-on: https://gitea.com/gitea/act/pulls/137
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: badhezi <zlilaharon@gmail.com>
Co-committed-by: badhezi <zlilaharon@gmail.com>
2025-05-12 17:17:50 +00:00
Jack Jackson
5302c25feb Add environment variables for OIDC token service (#674)
Resurrecting [this PR](https://gitea.com/gitea/act_runner/pulls/272) (a dependency of [this one](https://github.com/go-gitea/gitea/pull/33945)) after the original author [lost motivation](https://github.com/go-gitea/gitea/pull/25664#issuecomment-2737099259) - though, to be clear, all credit belongs to them, and all blame for mistakes or misunderstandings to me.

Co-authored-by: Søren L. Hansen <sorenisanerd@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/674
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jack Jackson <scubbojj@gmail.com>
Co-committed-by: Jack Jackson <scubbojj@gmail.com>
2025-05-08 01:58:31 +00:00
Christopher Homberger
a616ed1a10 feat: register interactive with values from cli (#682)
I used to be able to do something like `./act_runner register --instance https://gitea.com --token testdcff --name test` on GitHub Actions Runners, but act_runner always asked me to enter instance, token etc. again and requiring me to use `--no-interactive` including passing everything per cli.

My idea was to extract the preset input of some stages to skip the prompt for this value if it is a non empty string. Labels is the only question that has been asked more than once if validation failed, in this case the error path have to unset the values of the input structure to not end in a non-interactive loop.

_I have written this initially for my own gitea runner, might be useful to everyone using the official runner as well_

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/682
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2025-05-08 01:57:53 +00:00
Christopher Homberger
f0b5aff3bb fix: invalid label NoInteractive exit code (#683)
* add test
* return validation error not nil from function

Closes #665

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/683
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2025-05-07 17:17:26 +00:00
Christopher Homberger
44b4736703 feat: docker env vars for ephemeral and once (#685)
* GITEA_RUNNER_EPHEMERAL=1
* GITEA_RUNNER_ONCE=1

Related https://gitea.com/gitea/act_runner/issues/684

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/685
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2025-05-07 15:43:05 +00:00
Jack Jackson
65c232c4a5 Parse permissions (#133)
Resurrecting [this PR](https://gitea.com/gitea/act/pulls/73) as the original author has [lost motivation](https://github.com/go-gitea/gitea/pull/25664#issuecomment-2737099259) (though, to be clear - all credit belongs to them, all mistakes are mine and mine alone!)

Co-authored-by: Søren L. Hansen <sorenisanerd@gmail.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/133
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jack Jackson <scubbojj@gmail.com>
Co-committed-by: Jack Jackson <scubbojj@gmail.com>
2025-03-24 18:17:06 +00:00
Christopher Homberger
b1ae30dda8 ephemeral act runner (#649)
Works for both interactive and non-interactive registration mode.

A further enhancement would be jitconfig support of the daemon command, because after some changes in Gitea Actions the registration token became reusable.

removing runner and fail seems not possible at the current api level

Part of https://github.com/go-gitea/gitea/pull/33570

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/649
Reviewed-by: Zettat123 <zettat123@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2025-03-13 21:57:44 +00:00
techknowlogick
0d687268c7 act_runner requires go 1.24 now 2025-03-02 05:36:24 +00:00
techknowlogick
425a570261 use new docker image URLs (#661)
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/661
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.com>
Co-committed-by: techknowlogick <techknowlogick@gitea.com>
2025-03-01 20:21:52 +00:00
Lunny Xiao
4c8179ee12 upgrade to go1.24, act to 0.261.4 and actions-proto-go to 0.4.1 (#662)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/662
Reviewed-by: ChristopherHX <christopherhx@noreply.gitea.com>
2025-03-01 20:18:36 +00:00
Guillaume S.
5da4954b65 fix handle missing yaml.ScalarNode (#129)
This bug was reported on https://github.com/go-gitea/gitea/issues/33657
Rewrite of (see below) was missing in this commit 6cdf1e5788
```go
case string:
    acts[act] = []string{b}
```

Reviewed-on: https://gitea.com/gitea/act/pulls/129
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Guillaume S. <me@gsvd.dev>
Co-committed-by: Guillaume S. <me@gsvd.dev>
2025-02-26 06:19:02 +00:00
Pablo Carranza
5ae13f0bd7 Update xgo version to 1.24 (#651)
Co-authored-by: techknowlogick <techknowlogick@gitea.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/651
Co-authored-by: Pablo Carranza <pcarranza@gmail.com>
Co-committed-by: Pablo Carranza <pcarranza@gmail.com>
2025-02-15 16:07:18 +00:00
Zettat123
ec091ad269 Support concurrency (#124)
To support `concurrency` syntax for Gitea Actions

Gitea PR: https://github.com/go-gitea/gitea/pull/32751

Reviewed-on: https://gitea.com/gitea/act/pulls/124
Reviewed-by: Lunny Xiao <lunny@noreply.gitea.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2025-02-11 02:51:48 +00:00
Lunny Xiao
3510152e36 Fix Makefile make docker (#641)
Fix #640

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/641
2025-01-29 03:27:47 +00:00
armistace
8dfb805c62 Update examples/kubernetes/dind-docker.yaml to reflect recent changes to Dockerfile (#633)
With the changes made two months ago for the Dockerfile /opt/act/run.sh no longer exists in the docker container, this caused this example to fail, updating the example so that it correctly references run.sh now located in /usr/local/bin

I have used this to deploy on my own cluster and it is now working swimmingly

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/633
Reviewed-by: Zettat123 <zettat123@noreply.gitea.com>
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: armistace <armistace@noreply.gitea.com>
Co-committed-by: armistace <armistace@noreply.gitea.com>
2025-01-26 02:10:17 +00:00
Zettat123
a7080f5457 Update examples for GITEA_RUNNER_REGISTRATION_TOKEN env (#630)
For https://github.com/go-gitea/gitea/pull/32946

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/630
Reviewed-by: Lunny Xiao <lunny@noreply.gitea.com>
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2025-01-05 22:25:32 +00:00
techknowlogick
8b72d1c7ae add s390x and riscv64 as an arch for binaries 2024-12-09 18:49:38 +00:00
Zettat123
1656206765 Improve the support for reusable workflows (#122)
Fix [#32439](https://github.com/go-gitea/gitea/issues/32439)

- Support reusable workflows with conditional jobs
- Support nesting reusable workflows

Reviewed-on: https://gitea.com/gitea/act/pulls/122
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Jason Song <wolfogre@noreply.gitea.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2024-11-23 14:14:17 +00:00
garet90
8bc0275e74 feat: add once flag to daemon command (#19) (#598)
Once flag polls and completes one job then exits.

I use this with Windows Sandbox (and creating users with local brew install on Mac) to create a fresh environment every time.

Co-authored-by: Garet Halliday <garet@pit.dev>
Co-authored-by: Jason Song <wolfogre@noreply.gitea.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/598
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Reviewed-by: Jason Song <wolfogre@noreply.gitea.com>
Co-authored-by: garet90 <garet90@noreply.gitea.com>
Co-committed-by: garet90 <garet90@noreply.gitea.com>
2024-11-06 17:16:08 +00:00
Jason Song
0348aaac59 Wait for the Docker daemon to be ready before starting act runner (#620)
Follow #619.

Wait for the Docker daemon to be ready before starting act runner.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/620
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2024-11-06 07:38:31 +00:00
Jason Song
9712481bed Support basic, dind and dind-rootless as multiple kinds of images (#619)
- `basic`: Only the runner process in the container; users need to mount the Docker socket to it.
- `dind`: A Docker daemon will be started in the container with the root user.
- `dind-rootless`: A Docker daemon will be started in the container with a rootless user.

Use s6 instead of supervisord to start processes.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/619
Reviewed-by: Zettat123 <zettat123@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2024-11-06 03:15:51 +00:00
Lunny Xiao
b5f901b2d9 Upgrade act from v0.261.2 -> v0.261.3 (#607)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/607
Reviewed-by: Jason Song <wolfogre@noreply.gitea.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-10-18 04:33:57 +00:00
Lunny Xiao
6cdf1e5788 Fix ParseRawOn sequence problem (#119)
Fix https://gitea.com/gitea/act/actions/runs/277/jobs/0

Reviewed-on: https://gitea.com/gitea/act/pulls/119
2024-10-05 19:29:55 +00:00
Lunny Xiao
ab381649da Add parsing for workflow dispatch (#118)
Reviewed-on: https://gitea.com/gitea/act/pulls/118
2024-10-03 02:56:58 +00:00
frank-dspeed
0e2a3e00f5 examples/vm/rootless-docker.md aktualisiert (#487)
Depending on the VM's existing users the id can vary

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/487
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: frank-dspeed <frank-dspeed@noreply.gitea.com>
Co-committed-by: frank-dspeed <frank-dspeed@noreply.gitea.com>
2024-09-30 01:55:04 +00:00
Schallbert
b282356e9e update example for docker-compose to allow fix 502 errors in case Gitea not yet ready on runner startup (#605)
Minimalistic approach: Only adds what is needed to fix #600
Context: https://blog.schallbert.de/en/fix-gitea-runner/

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/605
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Zettat123 <zettat123@noreply.gitea.com>
Co-authored-by: Schallbert <schallbert@mailbox.org>
Co-committed-by: Schallbert <schallbert@mailbox.org>
2024-09-26 05:54:54 +00:00
Jason Song
b075e3a1d5 Bump goreleaser-action to use v2 by default (#604)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/604
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2024-09-24 08:46:21 +00:00
Jason Song
e27189ea32 Fix releasing (#603)
```
[command]/opt/hostedtoolcache/goreleaser-action/2.3.2-pro/x64/goreleaser release --nightly
  • by using this software you agree with its EULA, available at https://goreleaser.com/eula
  • running goreleaser v2.3.2-pro
  • only configurations files on  version: 2  are supported, yours is  version: 0 , please update your configuration
  ⨯ release failed after 0s                  error=only configurations files on  version: 2  are supported, yours is  version: 0 , please update your configuration
::error::The process '/opt/hostedtoolcache/goreleaser-action/2.3.2-pro/x64/goreleaser' failed with exit code 1
```

```
#20 [linux/arm64 builder 2/5] RUN apk add --no-cache make git
#20 CANCELED
------
 > [linux/amd64 builder 5/5] RUN make clean && make build:
0.058 go clean -x -i ./...
0.061 go: go.mod requires go >= 1.22 (running go 1.21.10; GOTOOLCHAIN=local)
0.061 make: *** [Makefile:176: clean] Error 1
------
 1 warning found (use docker --debug to expand):
 - FromAsCasing: 'as' and 'FROM' keywords' casing do not match (line 1)
Dockerfile:8
--------------------
   6 |     WORKDIR /opt/src/act_runner
   7 |
   8 | >>> RUN make clean && make build
   9 |
  10 |     FROM alpine:3.18
--------------------
ERROR: failed to solve: process "/bin/sh -c make clean && make build" did not complete successfully: exit code: 2
::error::buildx failed with: ERROR: failed to solve: process "/bin/sh -c make clean && make build" did not complete successfully: exit code: 2
```

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/603
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2024-09-24 08:22:53 +00:00
Jason Song
59e478464e Bump act to v0.261.2 (#602)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/602
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Zettat123 <zettat123@noreply.gitea.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2024-09-24 07:04:47 +00:00
Jason Song
38e7e9e939 Use hashed uses string as cache dir name (#117)
Reviewed-on: https://gitea.com/gitea/act/pulls/117
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-09-24 06:53:41 +00:00
Zettat123
2ab806053c Check all job results when calling reusable workflows (#116)
Fix [#31900](https://github.com/go-gitea/gitea/issues/31900)

Reviewed-on: https://gitea.com/gitea/act/pulls/116
Reviewed-by: Jason Song <wolfogre@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2024-09-24 06:52:45 +00:00
Zettat123
6a090f67e5 Support some GITEA_ environment variables (#112)
Fix https://gitea.com/gitea/act_runner/issues/575

Reviewed-on: https://gitea.com/gitea/act/pulls/112
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2024-07-29 04:17:45 +00:00
harryzcy
e1c7b20898 Bump Go to 1.22 and upgrade dependencies (#580)
Co-authored-by: Chongyi Zheng <git@zcy.dev>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/580
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: harryzcy <harryzcy@noreply.gitea.com>
Co-committed-by: harryzcy <harryzcy@noreply.gitea.com>
2024-07-26 16:00:33 +00:00
appleboy
2f78411c3d ci: standardize code style and update version extraction (#566)
- Change single quotes to double quotes in YAML files
- Update REPO_VERSION extraction method to use GITHUB_REF_NAME without the 'v' prefix

Signed-off-by: appleboy <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/566
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: appleboy <appleboy.tw@gmail.com>
Co-committed-by: appleboy <appleboy.tw@gmail.com>
2024-06-28 07:48:10 +00:00
rowan-allspice
d1d3cad4b0 feat: allow graceful shutdowns (#546)
Add a `Shutdown(context.Context) error` method to the Poller. Calling this method will first shutdown all active polling, preventing any new jobs from spawning. It will then wait for either all jobs to finish, or for the context to be cancelled. If the context is cancelled, it will then force all jobs to end, and then exit.

Fixes https://gitea.com/gitea/act_runner/issues/107

Co-authored-by: Rowan Bohde <rowan.bohde@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/546
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: rowan-allspice <rowan-allspice@noreply.gitea.com>
Co-committed-by: rowan-allspice <rowan-allspice@noreply.gitea.com>
2024-05-27 07:38:55 +00:00
rowan-allspice
1735b26e66 Don't log job output when debug logging is not enabled (#543)
We wanted the ability to disable outputting the logs from the individual job to the console. This changes the logging so that job logs are only output to the console whenever debug logging is enabled in `act_runner`, while still allowing the `Reporter` to receive these logs and forward them to Gitea when debug logging is not enabled.

Co-authored-by: Rowan Bohde <rowan.bohde@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/543
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: rowan-allspice <rowan-allspice@noreply.gitea.com>
Co-committed-by: rowan-allspice <rowan-allspice@noreply.gitea.com>
2024-05-07 05:58:33 +00:00
Lunny Xiao
65ed62d2f5 Upgrade dependencies (#537)
Patially fix #513

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/537
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-04-11 04:36:00 +00:00
Jason Song
ec03f19650 Bump act to v0.261.1 (#535)
Related to https://gitea.com/gitea/act/compare/v0.261.0...v0.261.1

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/535
2024-04-10 07:07:56 +00:00
Jason Song
8567324a19 Release nightly versions for tags (#536)
Or the latest nightly version could be "v0.2.8-4-gbe2df36" even though there's "v0.2.9", although they are for the same commit.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/536
2024-04-10 07:06:08 +00:00
Jason Song
517d11c671 Reduce log noise (#108)
Cannot guarantee that all noisy logs can be removed at once.

Comment them instead of removing them to make it easier to merge upstream.

What have been removed in this PR are those that are very very long and almost unreadable logs, like

<img width="839" alt="image" src="/attachments/b59e1dcc-4edd-4f81-b939-83dcc45f2ed2">

Reviewed-on: https://gitea.com/gitea/act/pulls/108
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-04-10 06:55:46 +00:00
Jason Song
e1b1e81124 Revert "Pass 'sleep' as container command rather than entrypoint (#86)" (#107)
This reverts #86.

Some images use a custom entry point for specific usage, then `[entrypoint] [cmd]` like `helm /bin/sleep 1` will failed.

It causes https://gitea.com/gitea/helm-chart/actions/runs/755 since the image is `alpine/helm`.

```yaml
  check-and-test:
    runs-on: ubuntu-latest
    container: alpine/helm:3.14.3
```

Reviewed-on: https://gitea.com/gitea/act/pulls/107
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-04-10 06:53:28 +00:00
Zettat123
64876e3696 Interpolate job name with matrix (#106)
Fix https://github.com/go-gitea/gitea/issues/28207

Reviewed-on: https://gitea.com/gitea/act/pulls/106
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2024-04-07 03:34:53 +00:00
Jason Song
be2df361ef Ensure declare to use new labels (#530)
Ensure "declare" is supported, to use new labels, see https://gitea.com/gitea/act_runner/pulls/529

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/530
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-04-02 07:39:40 +00:00
Jason Song
a5085dde0c Use gitea/runner-images by default (#529)
See https://gitea.com/gitea/runner-images

Also give up ubuntu-18.04 since it's too old. And enable force_pull by default to check new versions of images.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/529
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-04-02 07:38:14 +00:00
Jason Song
cef86d1140 Bump act to v0.260.0 (#528)
Related to:

- https://gitea.com/gitea/act/pulls/104
- https://gitea.com/gitea/act/issues/102

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/528
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2024-04-02 06:25:33 +00:00
Jason Song
3fa1dba92b Merge tag 'nektos/v0.2.61' 2024-04-01 14:23:16 +08:00
GitHub Actions
361b7e9f1a chore: bump VERSION to 0.2.61 2024-04-01 02:16:09 +00:00
Zettat123
9725f60394 Support reusing workflows with absolute URLs (#104)
Resolve https://gitea.com/gitea/act_runner/issues/507

Reviewed-on: https://gitea.com/gitea/act/pulls/104
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2024-03-29 06:15:28 +00:00
Jason Song
94c45acf6b Use node 20 by default (#526)
Because Octokit requires Node 18 or higher now.

See https://github.com/octokit/octokit.js/#fetch-missing .

However, for existing runners, users should manually modify `labels` in the `.runner` file or `runner.labels` in the `config.yaml` file to update. Don't forget to restart.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/526
Reviewed-by: sillyguodong <sillyguodong@noreply.gitea.com>
2024-03-29 03:03:26 +00:00
ChristopherHX
f825e42ce2 fix: cache adjust restore order of exact key matches (#2267)
* wip: adjust restore order

* fixup

* add tests

* cleanup

* fix typo

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-03-29 02:07:20 +00:00
Jason Collins
d9a19c8b02 Trivial: reduce log spam. (#2256)
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
2024-03-28 23:28:48 +00:00
James Kang
3949d74af5 chore: remove repetitive words (#2259)
Signed-off-by: majorteach <csgcgl@126.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
2024-03-28 23:14:53 +00:00
Jason Song
b9382a2c4e Support overwriting caches (#2265)
* feat: support overwrite caches

* test: fix case

* test: fix get_with_multiple_keys

* chore: use atomic.Bool

* test: improve get_with_multiple_keys

* chore: use ping to improve path

* fix: wrong CompareAndSwap

* test: TestHandler_gcCache

* chore: lint code

* chore: lint code
2024-03-28 16:42:02 +00:00
Jason Song
f56dd65ff6 test: use ping to improve network test (#2266) 2024-03-28 11:56:26 +00:00
Jason Song
1760899d27 Set gitea token to release (#524)
Follow a7eca813ea/.gitea/workflows/release-tag.yml (L39)

It works before unexpectedly because of https://github.com/nektos/act/pull/2089

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/524
2024-03-27 10:51:45 +00:00
Zettat123
a7eca813ea Bump act to v0.260.2 (#523)
Related to

- https://gitea.com/gitea/act/pulls/86
- https://gitea.com/gitea/act/pulls/103

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/523
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2024-03-27 10:25:23 +00:00
Thomas E Lackey
a79d81989f Pass 'sleep' as container command rather than entrypoint (#86)
The current code overrides the container's entrypoint with `sleep`.  Unfortunately, that prevents initialization scripts, such as to initialize Docker-in-Docker, from running.

The change simply moves the `sleep` to the command, rather than entrypoint, directive.

For most containers of this sort, the entrypoint script performs initialization, and then ends with `$@` to execute whatever command is passed.

If the container has no entrypoint, the command is executed directly.  As a result, this should be a transparent change for most use cases, while allowing the container's entrypoint to be used when present.

Reviewed-on: https://gitea.com/gitea/act/pulls/86
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Thomas E Lackey <telackey@bozemanpass.com>
Co-committed-by: Thomas E Lackey <telackey@bozemanpass.com>
2024-03-27 10:17:48 +00:00
Zettat123
655f578563 Remove the network when there is no service (#103)
Reviewed-on: https://gitea.com/gitea/act/pulls/103
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2024-03-27 10:07:29 +00:00
Jason Song
23ec12b8cf Bump act to v0.260.0 (#522)
Related to https://gitea.com/gitea/act/issues/99.

Also update other main dependencies.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/522
Reviewed-by: Zettat123 <zettat123@noreply.gitea.com>
2024-03-27 03:17:04 +00:00
Zettat123
0054a45d1b Fix bugs related to services (#100)
Related to #99

- use `networkNameForGitea` function instead of `networkName` to get network name
- add the missing `Cmd` and `AutoRemove` when creating service containers

Reviewed-on: https://gitea.com/gitea/act/pulls/100
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2024-03-26 10:14:06 +00:00
Jason Song
79a7577c15 Merge tag 'nektos/v0.2.60' 2024-03-25 16:58:11 +08:00
Jason Song
a28ebf0a48 Improve workflows (#98)
Starting from setup-go v4, it will cache build dependencies by default, see https://github.com/actions/setup-go#caching-dependency-files-and-build-outputs.

Also bump some versions.

Reviewed-on: https://gitea.com/gitea/act/pulls/98
2024-03-25 15:54:51 +08:00
Jason Song
2b860ce371 Remove emojis in command outputs (#97)
Remove emojis in command outputs; leave others since they don't matter.

Help https://github.com/go-gitea/gitea/pull/29777

Reviewed-on: https://gitea.com/gitea/act/pulls/97
2024-03-25 15:54:39 +08:00
Zettat123
3a9e7d18de Support cloning remote actions from insecure Gitea instances (#92)
Related to https://github.com/go-gitea/gitea/issues/28693

Reviewed-on: https://gitea.com/gitea/act/pulls/92
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2024-03-25 15:54:09 +08:00
Claudio Nicora
b4edc952d9 Patched options() to let container options propagate to job containers (#80)
This PR let "general" container config to be propagated to each job container.

See:
- https://gitea.com/gitea/act_runner/issues/265#issuecomment-744382
- https://gitea.com/gitea/act_runner/issues/79
- https://gitea.com/gitea/act_runner/issues/378

Reviewed-on: https://gitea.com/gitea/act/pulls/80
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Claudio Nicora <claudio.nicora@gmail.com>
Co-committed-by: Claudio Nicora <claudio.nicora@gmail.com>
2024-03-25 15:43:14 +08:00
sillyguodong
f1213213d8 Make runs-on support variable expression (#91)
Partial implementation of https://gitea.com/gitea/act_runner/issues/445, the Gitea side also needs a PR for the entire functionality.
Gitea side: https://github.com/go-gitea/gitea/pull/29468

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/91
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2024-03-25 15:42:59 +08:00
Zettat123
a1fc2b3ca7 Improve the explanation of workdir_parent config (#519)
Fix #512

act_runner adds a '/' before the path (see 5977042b86/internal/app/run/runner.go (L186)) . So `workdir_parent` doesn't need to have the prefix '/'.

If `workdir_parent` has the prefix '/', errors will occur when reading files from the job container.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/519
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2024-03-22 02:30:31 +00:00
dependabot[bot]
069720abff build(deps): bump github.com/docker/docker (#2252)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 24.0.7+incompatible to 24.0.9+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v24.0.7...v24.0.9)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-20 17:37:01 +00:00
Jason Song
5977042b86 Bump act to v0.259.2 (#515)
Related to https://gitea.com/gitea/act/pulls/97

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/515
Reviewed-by: silverwind <silverwind@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2024-03-14 18:58:43 +00:00
Jason Song
6bc19cbc33 Improve workflows (#516)
Starting from setup-go v4, it will cache build dependencies by default, see https://github.com/actions/setup-go#caching-dependency-files-and-build-outputs.

Also bump some versions.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/516
Reviewed-by: silverwind <silverwind@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2024-03-14 13:27:47 +00:00
dependabot[bot]
8c83d57212 build(deps): bump golang.org/x/term from 0.17.0 to 0.18.0 (#2244)
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.17.0 to 0.18.0.
- [Commits](https://github.com/golang/term/compare/v0.17.0...v0.18.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-11 02:28:21 +00:00
ChristopherHX
119ceb81d9 fix: rootless permission bits (new actions cache) (#2242)
* fix: rootless permission bits (new actions cache)

* add test

* fix lint / more tests
2024-03-08 01:25:03 +00:00
huajin tong
352ad41ad2 fix function name in comment (#2240)
Signed-off-by: thirdkeyword <fliterdashen@gmail.com>
2024-03-06 14:20:06 +00:00
Zettat123
75006a59cc Support cloning remote actions from insecure Gitea instances (#508)
Follow https://gitea.com/gitea/act/pulls/92

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/508
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2024-03-06 06:10:37 +00:00
ChristopherHX
75e4ad93f4 fix: docker buildx cache restore not working (#2236)
* To take effect artifacts v4 pr is needed with adjusted claims
2024-03-05 06:04:54 +00:00
dependabot[bot]
934b13a7a1 build(deps): bump github.com/stretchr/testify from 1.8.4 to 1.9.0 (#2235)
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.8.4 to 1.9.0.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.8.4...v1.9.0)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-03-04 03:08:44 +00:00
SupremeVoid
4da97f53de Bump act to v0.259.0 (#491)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/491
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: silverwind <silverwind@noreply.gitea.com>
Co-authored-by: SupremeVoid <supremevoid@t-online.de>
Co-committed-by: SupremeVoid <supremevoid@t-online.de>
2024-03-03 16:34:37 +00:00
sillyguodong
45270656df Set the status of steps to skipped if job is skipped (#500)
If a job is detected as skipped, its steps should also be `skipped`.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/500
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2024-03-01 08:33:32 +00:00
GitHub Actions
d3c8664d3d chore: bump VERSION to 0.2.60 2024-03-01 02:12:58 +00:00
dependabot[bot]
c79f59f802 build(deps): bump go.etcd.io/bbolt from 1.3.8 to 1.3.9 (#2229)
Bumps [go.etcd.io/bbolt](https://github.com/etcd-io/bbolt) from 1.3.8 to 1.3.9.
- [Release notes](https://github.com/etcd-io/bbolt/releases)
- [Commits](https://github.com/etcd-io/bbolt/compare/v1.3.8...v1.3.9)

---
updated-dependencies:
- dependency-name: go.etcd.io/bbolt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-26 03:18:59 +00:00
dependabot[bot]
68597d68f6 build(deps): bump github.com/rhysd/actionlint from 1.6.26 to 1.6.27 (#2228)
Bumps [github.com/rhysd/actionlint](https://github.com/rhysd/actionlint) from 1.6.26 to 1.6.27.
- [Release notes](https://github.com/rhysd/actionlint/releases)
- [Changelog](https://github.com/rhysd/actionlint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rhysd/actionlint/compare/v1.6.26...v1.6.27)

---
updated-dependencies:
- dependency-name: github.com/rhysd/actionlint
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-26 03:02:49 +00:00
ChristopherHX
e1cd7c915f fix: logo and demo asset urls (#2226) 2024-02-24 02:47:55 +00:00
ChristopherHX
ac6a465e27 feat: Test writing remote Actions locally (#2155)
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-02-23 07:40:14 +00:00
dependabot[bot]
9e6ce121bc build(deps): bump github.com/opencontainers/image-spec (#2223)
Bumps [github.com/opencontainers/image-spec](https://github.com/opencontainers/image-spec) from 1.1.0-rc6 to 1.1.0.
- [Release notes](https://github.com/opencontainers/image-spec/releases)
- [Changelog](https://github.com/opencontainers/image-spec/blob/main/RELEASES.md)
- [Commits](https://github.com/opencontainers/image-spec/compare/v1.1.0-rc6...v1.1.0)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/image-spec
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-19 03:04:51 +00:00
Christopher Homberger
e14f42c40a Add ACTIONS_RESULTS_URL to env (#473)
actions/upload-artifact@v4 and actions/download-artifact@v4 depend on this variable

BaseUrl in a url are ignored by the nodejs code of the new actions, so this change doesn't append the path of the older `ACTIONS_RUNTIME_URL`.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/473
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2024-02-19 02:30:54 +00:00
ChristopherHX
5601fb0e13 refactor: simpilfy go-git cache (#2208) 2024-02-18 03:53:22 +00:00
ChristopherHX
852959e1e1 feat: offline mode for new action cache (#2173)
* Try fetch update of the action, otherwise use cached version

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-02-12 12:51:37 +00:00
dependabot[bot]
27eb79bb5a build(deps): bump github.com/opencontainers/image-spec (#2214)
Bumps [github.com/opencontainers/image-spec](https://github.com/opencontainers/image-spec) from 1.1.0-rc3 to 1.1.0-rc6.
- [Release notes](https://github.com/opencontainers/image-spec/releases)
- [Changelog](https://github.com/opencontainers/image-spec/blob/main/RELEASES.md)
- [Commits](https://github.com/opencontainers/image-spec/compare/v1.1.0-rc3...v1.1.0-rc6)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/image-spec
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-02-12 02:26:14 +00:00
dependabot[bot]
8277584f00 build(deps): bump golang.org/x/term from 0.16.0 to 0.17.0 (#2213)
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/term/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 02:14:13 +00:00
Christopher Homberger
e6630e2e36 Use artifacts v4 jwt if available (#471)
Needs https://github.com/go-gitea/gitea/pull/28885 to provide jwt if sent by server

Could fix #459, but that has not been verified.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/471
Reviewed-by: delvh <dev.lh@web.de>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Co-committed-by: Christopher Homberger <christopher.homberger@web.de>
2024-02-08 02:43:39 +00:00
Kristofer
f2e65e1d40 fix: Rootless error concerning /var/run/docker.sock (#2181)
* Use same socket defaulting strategy every time

* Always default to DOCKER_HOST

* Add more debug logs

* Commenting, and massively simplified socket logic

* Rever to upstream run_context.go

* Fix EACCESS error regarding /opt/hostedtoolcache

* Revert "Fix EACCESS error regarding /opt/hostedtoolcache"

This reverts commit b2a8394d3358e1b5aab9dabe555d4a3f2bf0b2f9.

* Revert CLI debug logs

* Move socket and host handling to own function, and simplify logic

* Move to container package

* Make return be a struct

* Write tests to verify functionality

* Fix DOCKER_HOST being set to the string "DOCKER_HOST"

* Always use struct

* Use socketLocation, for DOCKER_HOST and more defaults

* Fixup arguments to GetSocketAndHost in test and root.go

* Un-struct hasDockerHost

* Fixup logic and set hasDockerHost

* Minor scoping & variable name change

* Move functionality to a new file

* Rename corresponding test

* Reviewfix

* Fix DOCKER_HOST expected

* Fix test assertions and add comments

* Swap comparison actual, expected

* Fixed no-DOCKER_HOST env test

* Fixed default socket test

* Add test to verify review comments

* Add more test for greater test coverage

* Consistent comment references

* Fix bug found while writing tests

* Passing tests

* NoMountNoHost testfix

* Rename test appropriately

* NoMount testfix

* Fixed OnlySocket

* Swap expected <-> actual in tests

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-02-06 17:18:11 +00:00
ChristopherHX
6e80373eb6 Shrink Readme (#2198)
nektosact.com should become the single source of truth
2024-02-06 08:22:01 -08:00
GitHub Actions
b7a8145d09 chore: bump VERSION to 0.2.59 2024-02-01 22:32:06 +00:00
Milo Moisson
12c0c4277a feat: correctly use the xdg library, which has the side effect to fix the config survey (#2195) 2024-02-01 13:57:16 -08:00
GitHub Actions
3ed38d8e8b chore: bump VERSION to 0.2.58 2024-02-01 02:12:33 +00:00
dependabot[bot]
0dbf44c657 build(deps): bump github.com/opencontainers/runc from 1.1.7 to 1.1.12 (#2187)
Bumps [github.com/opencontainers/runc](https://github.com/opencontainers/runc) from 1.1.7 to 1.1.12.
- [Release notes](https://github.com/opencontainers/runc/releases)
- [Changelog](https://github.com/opencontainers/runc/blob/v1.1.12/CHANGELOG.md)
- [Commits](https://github.com/opencontainers/runc/compare/v1.1.7...v1.1.12)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/runc
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-31 23:56:50 +00:00
dependabot[bot]
6dcf9bc6e6 build(deps): bump github.com/moby/buildkit from 0.11.5 to 0.12.5 (#2188)
Bumps [github.com/moby/buildkit](https://github.com/moby/buildkit) from 0.11.5 to 0.12.5.
- [Release notes](https://github.com/moby/buildkit/releases)
- [Commits](https://github.com/moby/buildkit/compare/v0.11.5...v0.12.5)

---
updated-dependencies:
- dependency-name: github.com/moby/buildkit
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-31 23:41:20 +00:00
dependabot[bot]
df61c7fcdb build(deps): bump github.com/containerd/containerd from 1.6.19 to 1.6.26 (#2189)
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.6.19 to 1.6.26.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.6.19...v1.6.26)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-31 23:24:57 +00:00
dependabot[bot]
36e0261150 build(deps): bump github.com/opencontainers/image-spec (#2167)
Bumps [github.com/opencontainers/image-spec](https://github.com/opencontainers/image-spec) from 1.1.0-rc5 to 1.1.0-rc.6.
- [Release notes](https://github.com/opencontainers/image-spec/releases)
- [Changelog](https://github.com/opencontainers/image-spec/blob/main/RELEASES.md)
- [Commits](https://github.com/opencontainers/image-spec/compare/v1.1.0-rc5...v1.1.0-rc6)

---
updated-dependencies:
- dependency-name: github.com/opencontainers/image-spec
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-31 23:11:39 +00:00
dependabot[bot]
46dc2ffe80 build(deps): bump github.com/moby/buildkit from 0.12.4 to 0.12.5 (#2186)
Bumps [github.com/moby/buildkit](https://github.com/moby/buildkit) from 0.12.4 to 0.12.5.
- [Release notes](https://github.com/moby/buildkit/releases)
- [Commits](https://github.com/moby/buildkit/compare/v0.12.4...v0.12.5)

---
updated-dependencies:
- dependency-name: github.com/moby/buildkit
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-31 22:58:02 +00:00
Markus Wolf
054caec791 fix: use correct path to toolcache (#1494)
The toolcache on GitHub Actions need to be in
/opt/hostedtoolcache. This is the case for all
environment variables set by act, but it's not the
case for the volume mounted into the container.

Co-authored-by: Björn Brauer <zaubernerd@zaubernerd.de>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-30 22:43:52 +00:00
davidfrickert
f1f9142a3c fix: Exit docker container on any process exit (#462)
## Description
Issue described in #460

## Changes

- Edited `supervisord.conf` to exit if it detects any of the supervisored processes exiting.
- minor text fix

## Notes

Without this change (or something similar), if act_runner fails, then the container will stay up as a zombie container - it does nothing and does not restart. After this change, if act_runner fails (e.g. due to Gitea instance being down), then supervisord will exit and the container will be restarted.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/462
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: davidfrickert <david.frickert@protonmail.com>
Co-committed-by: davidfrickert <david.frickert@protonmail.com>
2024-01-30 13:47:42 +00:00
ChristopherHX
5a80a044f9 refactor: filecollector into new package (#2174)
* refactor: filecollector into new package

* Add test for symlinks

* add test fix bug of GetContainerArchive

* add test data
2024-01-30 00:46:45 +00:00
dependabot[bot]
4ca35d2192 build(deps): bump codecov/codecov-action from 3.1.4 to 3.1.5 (#2175)
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3.1.4 to 3.1.5.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v3.1.4...v3.1.5)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-29 02:34:33 +00:00
Josh Soref
5e0d29d665 fix: improve warning about remote not found (#2169)
Signed-off-by: Josh Soref <2119212+jsoref@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-28 19:21:21 +00:00
ChristopherHX
6dd67253bc fix: improve new-action-cache fetch failure error (#2172)
- include repoURL and repoRef in error
- map NoErrAlreadyUptodate to `couldn't find remote ref` for branchOrtag
  fetch request

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-28 17:02:15 +00:00
ChristopherHX
09d4b5d6ad fix: subpath actions via new artifact cache (#2170)
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-28 16:49:47 +00:00
ChristopherHX
a6ec2c129a fix: improve action not found error (#2171) 2024-01-28 16:37:19 +00:00
Eng Zer Jun
424fd5e02b refactor(cmd/root): simplify parseEnvs (#2162)
Prior to this commit, `parseEnvs` accept two parameters:

  1. env []string
  2. envs map[string]string

`parseEnvs` then do a `nil` check for `env`. However, we never pass a
`nil` `env` to `parseEnvs` in `newRunCommand`.

This commit simplify the `parseEnvs` function to accept just one
`env []string` parameter and return the result as `map[string]string`
instead.

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2024-01-24 03:06:31 +00:00
Matthew
6a8c42ac53 Add containerd's normalized architectures to archMapper (#2168) 2024-01-24 02:44:48 +00:00
dependabot[bot]
c215e0888a build(deps): bump megalinter/megalinter from 7.7.0 to 7.8.0 (#2164)
Bumps [megalinter/megalinter](https://github.com/megalinter/megalinter) from 7.7.0 to 7.8.0.
- [Release notes](https://github.com/megalinter/megalinter/releases)
- [Changelog](https://github.com/oxsecurity/megalinter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/megalinter/megalinter/compare/v7.7.0...v7.8.0)

---
updated-dependencies:
- dependency-name: megalinter/megalinter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-22 02:22:08 +00:00
Milo Moisson
6091094e14 fix: write default config in XDG config dir to avoid cluttering the HOME directory by default (#2140)
Co-authored-by: Casey Lee <cplee@nektos.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-21 20:16:06 +00:00
胖梁
8072a00a77 WorkflowDispatchConfig supports multiple yaml node kinds (#2123)
* WorkflowDispatchConfig supports ScalarNode and SequenceNode yaml node kinds

* Avoid using log.Fatal

* package slices is not in golang 1.20

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-20 14:07:36 +00:00
Kristoffer
7f7d84b10f fix: match cache restore-keys in creation reverse order (#2153)
* Match cache restore-keys in creation reverse order

* Match full prefix when selecting cache

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-20 12:11:50 +00:00
dependabot[bot]
15bb54f14e build(deps): bump actions/upload-artifact from 3 to 4 (#2133)
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Casey Lee <cplee@nektos.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-20 00:32:38 +00:00
TKaxv_7S
f055d4ae60 feat: support offline mode (#2128)
* Add: Actions Offline Mode

* Add: Actions Offline Mode

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-20 00:20:15 +00:00
ChristopherHX
f7a846d2f5 feat: cli option to enable the new action cache (#1954)
* Enable the new action cache

* fix

* fix: CopyTarStream (Docker)

* suppress panic in test

* add a cli option for opt in

* fixups

* add package

* fix

* rc.Config nil in test???

* add feature flag

* patch

* Fix respect --action-cache-path

Co-authored-by: Björn Brauer <zaubernerd@zaubernerd.de>

* add remote reusable workflow to ActionCache

* fixup

---------

Co-authored-by: Björn Brauer <zaubernerd@zaubernerd.de>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-01-19 23:49:35 +00:00
Casey Lee
cd40f3fe9b ci: automatic merge PRs created by a maintainer and approved by 1 other maintainer (#2156) 2024-01-19 07:04:05 -08:00
Leonardo Taccari
adbe229fcb Add support for NetBSD (#2023)
NetBSD can run Docker CLI and then use Docker on some remote machine
via DOCKER_HOST.

(This can be probably extended to all other Unix-es capable of running
just Docker CLI.)

Co-authored-by: ChristopherHX <christopher.homberger@web.de>
2024-01-08 19:26:03 +00:00
dependabot[bot]
96d6cf8b2c build(deps): bump github.com/cloudflare/circl from 1.3.3 to 1.3.7 (#2149)
Bumps [github.com/cloudflare/circl](https://github.com/cloudflare/circl) from 1.3.3 to 1.3.7.
- [Release notes](https://github.com/cloudflare/circl/releases)
- [Commits](https://github.com/cloudflare/circl/compare/v1.3.3...v1.3.7)

---
updated-dependencies:
- dependency-name: github.com/cloudflare/circl
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-08 17:01:44 +00:00
dependabot[bot]
ef5746ba74 build(deps): bump golang.org/x/term from 0.15.0 to 0.16.0 (#2148)
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.15.0 to 0.16.0.
- [Commits](https://github.com/golang/term/compare/v0.15.0...v0.16.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-08 02:23:22 +00:00
GitHub Actions
4fae81efe4 chore: bump VERSION to 0.2.57 2024-01-01 02:17:35 +00:00
Alex Lau(AvengerMoJo)
f17cad1bbe Update the docker image rebuild flag for config file and example gitea/act_runner#390 (#391)
this is a PR for the issue gitea/act_runner#390
It adding configuration for dockerfile container image to rebuild base on the configuration flag force_rebuild in config.yaml

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/391
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Alex Lau(AvengerMoJo) <avengermojo@gmail.com>
Co-committed-by: Alex Lau(AvengerMoJo) <avengermojo@gmail.com>
2023-12-20 07:13:33 +00:00
hakito
daf52d0e62 Sanitize UFT-8 content in logs (#453)
I accidently closed my previous PR #384

This PR replaces invalid UTF-8 character in a stream with `?` character. On Windows Server 2019 other characters are replaced by `?` as well so it's consistent.

fixes #452

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/453
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: hakito <hakito@noreply.gitea.com>
Co-committed-by: hakito <hakito@noreply.gitea.com>
2023-12-20 07:06:46 +00:00
dependabot[bot]
238a495579 build(deps): bump golang.org/x/crypto from 0.16.0 to 0.17.0 (#2134)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.16.0 to 0.17.0.
- [Commits](https://github.com/golang/crypto/compare/v0.16.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 21:45:57 +00:00
dependabot[bot]
74dcce467d build(deps): bump github.com/containerd/containerd from 1.7.2 to 1.7.11 (#2136)
Bumps [github.com/containerd/containerd](https://github.com/containerd/containerd) from 1.7.2 to 1.7.11.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v1.7.2...v1.7.11)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-19 21:32:29 +00:00
GitHub Actions
0c60f9749a chore: bump VERSION to 0.2.56 2023-12-17 22:10:10 +00:00
dependabot[bot]
6b0ef97c52 build(deps): bump actions/stale from 8 to 9 (#2120)
Bumps [actions/stale](https://github.com/actions/stale) from 8 to 9.
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/stale/compare/v8...v9)

---
updated-dependencies:
- dependency-name: actions/stale
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-12-17 09:14:56 +00:00
raffis
0806c8b109 feat: support config env expansion (#2063)
Signed-off-by: Raffael Sahli <raffael.sahli@doodle.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-12-17 08:59:13 +00:00
dependabot[bot]
0dfb06748e build(deps): bump actions/checkout from 3 to 4 (#1998)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-12-16 23:47:32 +00:00
dependabot[bot]
603b44b585 build(deps): bump github.com/creack/pty from 1.1.20 to 1.1.21 (#2099)
Bumps [github.com/creack/pty](https://github.com/creack/pty) from 1.1.20 to 1.1.21.
- [Release notes](https://github.com/creack/pty/releases)
- [Commits](https://github.com/creack/pty/compare/v1.1.20...v1.1.21)

---
updated-dependencies:
- dependency-name: github.com/creack/pty
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
2023-12-16 23:18:33 +00:00
Jon Jensen
00fbfa754c Fix noisy runs-on error logging (#2102)
Move the logging back up a level to fix a minor logging issue introduced in #2088

`RunContext`s for composite actions have dummy/blank `Job`s with no `runs-on`,
meaning their calls to `withGithubEnv` would result in an inaccurate log message
complaining that `'runs-on' key not defined in ...`

Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-12-16 23:04:54 +00:00
ChristopherHX
1b10028447 fix: IsHost is defined as false on windows (#2093)
* fix: IsHost is defined as false on windows

* Update docker_run.go

* Update docker_run.go
2023-12-16 14:46:17 -08:00
dependabot[bot]
9cecf94039 build(deps): bump actions/setup-go from 4 to 5 (#2118)
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 4 to 5.
- [Release notes](https://github.com/actions/setup-go/releases)
- [Commits](https://github.com/actions/setup-go/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/setup-go
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-12-11 03:07:56 +00:00
dependabot[bot]
29f4123b5c build(deps): bump megalinter/megalinter from 7.6.0 to 7.7.0 (#2119)
Bumps [megalinter/megalinter](https://github.com/megalinter/megalinter) from 7.6.0 to 7.7.0.
- [Release notes](https://github.com/megalinter/megalinter/releases)
- [Changelog](https://github.com/oxsecurity/megalinter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/megalinter/megalinter/compare/v7.6.0...v7.7.0)

---
updated-dependencies:
- dependency-name: megalinter/megalinter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-11 02:53:36 +00:00
dependabot[bot]
85c3b3b541 build(deps): bump github.com/go-git/go-git/v5 from 5.10.1 to 5.11.0 (#2117)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.10.1 to 5.11.0.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Commits](https://github.com/go-git/go-git/compare/v5.10.1...v5.11.0)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-11 02:29:35 +00:00
dependabot[bot]
2b47c99bb7 build(deps): bump golang.org/x/term from 0.14.0 to 0.15.0 (#2112)
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.14.0 to 0.15.0.
- [Commits](https://github.com/golang/term/compare/v0.14.0...v0.15.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 02:49:24 +00:00
dependabot[bot]
3c405a0d94 build(deps): bump github.com/go-git/go-git/v5 from 5.10.0 to 5.10.1 (#2114)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.10.0 to 5.10.1.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Commits](https://github.com/go-git/go-git/compare/v5.10.0...v5.10.1)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-12-04 02:37:03 +00:00
dependabot[bot]
899a1f206e build(deps): bump github.com/moby/buildkit from 0.12.3 to 0.12.4 (#2113)
Bumps [github.com/moby/buildkit](https://github.com/moby/buildkit) from 0.12.3 to 0.12.4.
- [Release notes](https://github.com/moby/buildkit/releases)
- [Commits](https://github.com/moby/buildkit/compare/v0.12.3...v0.12.4)

---
updated-dependencies:
- dependency-name: github.com/moby/buildkit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-12-04 02:24:02 +00:00
GitHub Actions
95ff5bf299 chore: bump VERSION to 0.2.55 2023-12-01 02:17:43 +00:00
Renovate Bot
8c8a8ce401 fix(deps): update module github.com/avast/retry-go/v4 to v4.5.1 (#411)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-28 05:46:05 +00:00
Renovate Bot
08c681be0c fix(deps): update module golang.org/x/time to v0.5.0 (#429)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-28 05:45:28 +00:00
Renovate Bot
91bfe4c186 fix(deps): update module golang.org/x/time to v0.4.0 (#424)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-26 00:01:51 +00:00
Renovate Bot
825c6f97b7 fix(deps): update module github.com/mattn/go-isatty to v0.0.20 (#414)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-24 08:33:37 +00:00
Renovate Bot
2f3e5c7125 fix(deps): update module github.com/docker/docker to v24.0.7+incompatible (#413)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-24 08:27:38 +00:00
Renovate Bot
4d9de6ca8c fix(deps): update module github.com/spf13/cobra to v1.8.0 (#416)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-24 08:27:07 +00:00
Renovate Bot
feb39666cc fix(deps): update module code.gitea.io/gitea-vet to v0.2.3 (#410)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-24 08:26:24 +00:00
techknowlogick
61cd71c9f8 bump to actions/checkout v4 2023-11-24 03:06:21 +00:00
Renovate Bot
0adfc1c7cc chore(deps): update actions/setup-go action to v4 (#418)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-24 03:05:33 +00:00
Renovate Bot
e3c68668fa chore(deps): update docker/build-push-action action to v5 (#419)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-24 03:05:28 +00:00
Renovate Bot
f1b27d5274 chore(deps): update docker/login-action action to v3 (#420)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-24 03:05:22 +00:00
Renovate Bot
655a39fd61 chore(deps): update docker/setup-buildx-action action to v3 (#421)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-24 03:05:07 +00:00
Renovate Bot
cca7d54117 chore(deps): update docker/setup-qemu-action action to v3 (#422)
Co-authored-by: Renovate Bot <renovate-bot@gitea.com>
Co-committed-by: Renovate Bot <renovate-bot@gitea.com>
2023-11-24 03:05:00 +00:00
infinoid
934471813a Fix #404: nil map error when reading env file (#405)
Co-authored-by: Mark Glines <mark@glines.org>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/405
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: infinoid <infinoid@noreply.gitea.com>
Co-committed-by: infinoid <infinoid@noreply.gitea.com>
2023-11-24 01:56:27 +00:00
pat-s
1e940f028b Add renovate config (#408)
+ write access required for `renovate-bot` user

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/408
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: pat-s <patrick.schratz@gmail.com>
Co-committed-by: pat-s <patrick.schratz@gmail.com>
2023-11-23 20:41:10 +00:00
dependabot[bot]
bd10c9a801 build(deps): bump megalinter/megalinter from 7.5.0 to 7.6.0 (#2098)
Bumps [megalinter/megalinter](https://github.com/megalinter/megalinter) from 7.5.0 to 7.6.0.
- [Release notes](https://github.com/megalinter/megalinter/releases)
- [Changelog](https://github.com/oxsecurity/megalinter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/megalinter/megalinter/compare/v7.5.0...v7.6.0)

---
updated-dependencies:
- dependency-name: megalinter/megalinter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-11-20 03:19:49 +00:00
dependabot[bot]
3d0cb3d82b build(deps): bump actions/github-script from 6 to 7 (#2097)
Bumps [actions/github-script](https://github.com/actions/github-script) from 6 to 7.
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](https://github.com/actions/github-script/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/github-script
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-20 02:53:47 +00:00
GitHub Actions
7693697f4c chore: bump VERSION to 0.2.54 2023-11-14 11:52:13 +00:00
dependabot[bot]
4dcb9b7a13 build(deps): bump golang.org/x/term from 0.13.0 to 0.14.0 (#2091)
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.13.0 to 0.14.0.
- [Commits](https://github.com/golang/term/compare/v0.13.0...v0.14.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-13 02:57:59 +00:00
Jon Jensen
55477899e7 Evaluate if condition when calling a reusable workflow (#2087)
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
2023-11-12 20:01:32 +00:00
raffis
04011b6b78 feat: support runs-on labels and group (#2062)
Signed-off-by: Raffael Sahli <raffael.sahli@doodle.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
2023-11-12 19:46:38 +00:00
Björn Brauer
c8f847d82d Evaluate all service values (#2054)
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-11-12 18:30:21 +00:00
ChristopherHX
74b0fe8ba9 fix: (#2075)
network-scoped alias is supported only for containers in user defined networks

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-11-12 18:09:25 +00:00
Jon Jensen
18b4714e38 Don't set GITHUB_TOKEN (#2089)
This needs to be explicitly in the `env` to be consistent with GitHub

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-11-12 17:52:08 +00:00
Jon Jensen
610358e1c3 Support array expressions in runs-on (#2088)
* Support array expressions in runs-on

* Simplify appproach to use EvaluateYamlNode, fix case-sensitivity bug

---------

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-11-12 17:40:06 +00:00
Andreas Taylor
1c16fd1967 Use unique name for reusable workflow (#2015)
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
2023-11-12 17:21:41 +00:00
lng2020
2020ce79bf Remove ACTIONS_RUNTIME_TOKEN workaround (#396)
The bug has been fixed for a long time in the GitHub version.
The fix commit is d8823bfaed and released in the 4.0.0 and after. The issue also mentions that https://gitea.com/gitea/act_runner/issues/119#issuecomment-738294

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/396
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: lng2020 <nanguanlin6@gmail.com>
Co-committed-by: lng2020 <nanguanlin6@gmail.com>
2023-11-07 05:15:14 +00:00
dependabot[bot]
55b09a04cd build(deps): bump github.com/spf13/cobra from 1.7.0 to 1.8.0 (#2079)
Bumps [github.com/spf13/cobra](https://github.com/spf13/cobra) from 1.7.0 to 1.8.0.
- [Release notes](https://github.com/spf13/cobra/releases)
- [Commits](https://github.com/spf13/cobra/compare/v1.7.0...v1.8.0)

---
updated-dependencies:
- dependency-name: github.com/spf13/cobra
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-11-06 02:50:57 +00:00
Jason Song
5a79256ee4 fix: panic (#2071)
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-11-04 14:10:53 +00:00
GitHub Actions
1bb2ee7098 chore: bump VERSION to 0.2.53 2023-11-01 02:13:36 +00:00
Jason Song
00e9b3d62b Bump act (#394)
Bump act to follow https://gitea.com/gitea/act/pulls/81

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/394
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-10-31 15:03:07 +00:00
Jason Song
15045b4fc0 Merge pull request 'Fix panic in extractFromImageEnv' (#81) from wolfogre/act:bugfix/panic_extractFromImageEnv into main
Reviewed-on: https://gitea.com/gitea/act/pulls/81
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-10-31 14:48:40 +00:00
Jason Song
67918333fa fix: panic 2023-10-31 22:32:21 +08:00
dependabot[bot]
84a4025bc8 build(deps): bump github.com/docker/docker (#2067)
Bumps [github.com/docker/docker](https://github.com/docker/docker) from 24.0.6+incompatible to 24.0.7+incompatible.
- [Release notes](https://github.com/docker/docker/releases)
- [Commits](https://github.com/docker/docker/compare/v24.0.6...v24.0.7)

---
updated-dependencies:
- dependency-name: github.com/docker/docker
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-10-30 03:54:16 +00:00
dependabot[bot]
fb4f29fd6d build(deps): bump github.com/creack/pty from 1.1.18 to 1.1.20 (#2068)
Bumps [github.com/creack/pty](https://github.com/creack/pty) from 1.1.18 to 1.1.20.
- [Release notes](https://github.com/creack/pty/releases)
- [Commits](https://github.com/creack/pty/compare/v1.1.18...v1.1.20)

---
updated-dependencies:
- dependency-name: github.com/creack/pty
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-10-30 03:33:28 +00:00
dependabot[bot]
3e5c62977f build(deps): bump megalinter/megalinter from 7.4.0 to 7.5.0 (#2070)
Bumps [megalinter/megalinter](https://github.com/megalinter/megalinter) from 7.4.0 to 7.5.0.
- [Release notes](https://github.com/megalinter/megalinter/releases)
- [Changelog](https://github.com/oxsecurity/megalinter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/megalinter/megalinter/compare/v7.4.0...v7.5.0)

---
updated-dependencies:
- dependency-name: megalinter/megalinter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-10-30 03:16:31 +00:00
dependabot[bot]
83bfbcdafd build(deps): bump go.etcd.io/bbolt from 1.3.7 to 1.3.8 (#2065)
Bumps [go.etcd.io/bbolt](https://github.com/etcd-io/bbolt) from 1.3.7 to 1.3.8.
- [Release notes](https://github.com/etcd-io/bbolt/releases)
- [Commits](https://github.com/etcd-io/bbolt/compare/v1.3.7...v1.3.8)

---
updated-dependencies:
- dependency-name: go.etcd.io/bbolt
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-10-30 03:01:40 +00:00
dependabot[bot]
3d65b0f73f build(deps): bump github.com/docker/cli (#2069)
Bumps [github.com/docker/cli](https://github.com/docker/cli) from 24.0.6+incompatible to 24.0.7+incompatible.
- [Commits](https://github.com/docker/cli/compare/v24.0.6...v24.0.7)

---
updated-dependencies:
- dependency-name: github.com/docker/cli
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-10-30 02:46:59 +00:00
dependabot[bot]
854e3e9ec5 build(deps): bump github.com/go-git/go-git/v5 from 5.9.0 to 5.10.0 (#2066)
Bumps [github.com/go-git/go-git/v5](https://github.com/go-git/go-git) from 5.9.0 to 5.10.0.
- [Release notes](https://github.com/go-git/go-git/releases)
- [Commits](https://github.com/go-git/go-git/compare/v5.9.0...v5.10.0)

---
updated-dependencies:
- dependency-name: github.com/go-git/go-git/v5
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-30 02:28:08 +00:00
dependabot[bot]
db71c41d17 build(deps): bump github.com/mattn/go-isatty from 0.0.19 to 0.0.20 (#2059)
Bumps [github.com/mattn/go-isatty](https://github.com/mattn/go-isatty) from 0.0.19 to 0.0.20.
- [Commits](https://github.com/mattn/go-isatty/compare/v0.0.19...v0.0.20)

---
updated-dependencies:
- dependency-name: github.com/mattn/go-isatty
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 03:25:32 +00:00
dependabot[bot]
db6e477e25 build(deps): bump github.com/moby/buildkit from 0.12.2 to 0.12.3 (#2060)
Bumps [github.com/moby/buildkit](https://github.com/moby/buildkit) from 0.12.2 to 0.12.3.
- [Release notes](https://github.com/moby/buildkit/releases)
- [Commits](https://github.com/moby/buildkit/compare/v0.12.2...v0.12.3)

---
updated-dependencies:
- dependency-name: github.com/moby/buildkit
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-23 03:05:34 +00:00
Sam Foo
ceeb6c160c Add support for service containers (#1949)
* Support services (#42)

Removed createSimpleContainerName and AutoRemove flag

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/42
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>

* Support services options (#45)

Reviewed-on: https://gitea.com/gitea/act/pulls/45
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>

* Support intepolation for `env` of `services` (#47)

Reviewed-on: https://gitea.com/gitea/act/pulls/47
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>

* Support services `credentials` (#51)

If a service's image is from a container registry requires authentication, `act_runner` will need `credentials` to pull the image, see [documentation](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idcredentials).
Currently, `act_runner` incorrectly uses the `credentials` of `containers` to pull services' images and the `credentials` of services won't be used, see the related code: 0c1f2edb99/pkg/runner/run_context.go (L228-L269)

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/51
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>

* Add ContainerMaxLifetime and ContainerNetworkMode options

from: b9c20dcaa4

* Fix container network issue (#56)

Follow: https://gitea.com/gitea/act_runner/pulls/184
Close https://gitea.com/gitea/act_runner/issues/177

- `act` create new networks only if the value of `NeedCreateNetwork` is true, and remove these networks at last. `NeedCreateNetwork` is passed by `act_runner`. 'NeedCreateNetwork' is true only if  `container.network` in the configuration file of the `act_runner` is empty.
- In the `docker create` phase, specify the network to which containers will connect. Because, if not specify , container will connect to `bridge` network which is created automatically by Docker.
  - If the network is user defined network ( the value of `container.network` is empty or `<custom-network>`.  Because, the network created by `act` is also user defined network.), will also specify alias by `--network-alias`. The alias of service is `<service-id>`. So we can be access service container by `<service-id>:<port>` in the steps of job.
- Won't try to `docker network connect ` network after `docker start` any more.
  - Because on the one hand,  `docker network connect` applies only to user defined networks, if try to `docker network connect host <container-name>` will return error.
  - On the other hand, we just specify network in the stage of `docker create`, the same effect can be achieved.
- Won't try to remove containers and networks berfore  the stage of `docker start`, because the name of these containers and netwoks won't be repeat.

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/56
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>

* Check volumes (#60)

This PR adds a `ValidVolumes` config. Users can specify the volumes (including bind mounts) that can be mounted to containers by this config.

Options related to volumes:
- [jobs.<job_id>.container.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainervolumes)
- [jobs.<job_id>.services.<service_id>.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idvolumes)

In addition, volumes specified by `options` will also be checked.

Currently, the following default volumes (see a72822b3f8/pkg/runner/run_context.go (L116-L166)) will be added to `ValidVolumes`:
- `act-toolcache`
- `<container-name>` and `<container-name>-env`
- `/var/run/docker.sock` (We need to add a new configuration to control whether the docker daemon can be mounted)

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/60
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>

* Remove ContainerMaxLifetime; fix lint

* Remove unused ValidVolumes

* Remove ConnectToNetwork

* Add docker stubs

* Close docker clients to prevent file descriptor leaks

* Fix the error when removing network in self-hosted mode (#69)

Fixes https://gitea.com/gitea/act_runner/issues/255

Reviewed-on: https://gitea.com/gitea/act/pulls/69
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>

* Move service container and network cleanup to rc.cleanUpJobContainer

* Add --network flag; default to host if not using service containers or set explicitly

* Correctly close executor to prevent fd leak

* Revert to tail instead of full path

* fix network duplication

* backport networkingConfig for aliaes

* don't hardcode netMode host

* Convert services test to table driven tests

* Add failing tests for services

* Expose service container ports onto the host

* Set container network mode in artifacts server test to host mode

* Log container network mode when creating/starting a container

* fix: Correctly handle ContainerNetworkMode

* fix: missing service container network

* Always remove service containers

Although we usually keep containers running if the workflow errored
(unless `--rm` is given) in order to facilitate debugging and we have
a flag (`--reuse`) to always keep containers running in order to speed
up repeated `act` invocations, I believe that these should only apply
to job containers and not service containers, because changing the
network settings on a service container requires re-creating it anyway.

* Remove networks only if no active endpoints exist

* Ensure job containers are stopped before starting a new job

* fix: go build -tags WITHOUT_DOCKER

---------

Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: ZauberNerd <zaubernerd@zaubernerd.de>
2023-10-19 09:24:52 +00:00
haxwithaxe
130b9f1877 Added environment variable for run.sh to specify runner state file. (#377)
Added environment variable `RUNNER_STATE_FILE` to let users specify
where `run.sh` looks for the runner JSON file. Defaults to ``.runner``
to preserve the original behavior.

Addresses issue #368. It's not my preferred solution but it's the least invasive one I can think of.

I'm happy to make any changes you want.

I didn't see an appropriate place to reference the change in the documentation. I will add documentation wherever you think is appropriate.

To test this I did the following:
* Built an image with the `make docker` command and pushed it to my private registry.
* Added that private image as the image in my existing docker stack that was exhibiting the behavior described in #368.
* Added the RUNNER_STATE_FILE environment variable pointing to the runner JSON file set in my `config.yml` (``/data/runner.json`` in this case).
* Configured a new runner in gitea and added the token as an environment variable in the stack config.
* Deployed the stack and verified the new runner was recognized (in the idle state).
* Force updated the runner service to restart the container.
* Verified the same runner was still recognized in gitea (in the idle state) once it was back up and running.

Here is the relevant config. It's stored as a template so I've left the things that would normally be redacted as they are in the template.

```
    runner:
     image: hub.hax.in.net/haxwithaxe/act_runner:dev1
      networks:
        - swarm-net
      environment:
       RUNNER_STATE_FILE: /data/runner.json
        CONFIG_FILE: /data/config.yml
        GITEA_INSTANCE_URL: "https://git_gitea"
        GITEA_RUNNER_REGISTRATION_TOKEN: "{{ git_runner_reg_token }}"
        GITEA_RUNNER_NAME: "git_runner"
      volumes:
        - runner_data:/data
        - /var/run/docker.sock:/var/run/docker.sock
```

`runner_data` is a glusterfs subvolume

Thanks for creating this gitea specific fork. Apart from the issue this pull request addresses it works so well I don't have to think about it once it's set up.

Co-authored-by: haxwithaxe <spam@haxwithaxe.net>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/377
Co-authored-by: haxwithaxe <haxwithaxe@noreply.gitea.com>
Co-committed-by: haxwithaxe <haxwithaxe@noreply.gitea.com>
2023-10-15 23:21:53 +00:00
Jon Jensen
ace4cd47c7 Fix float formatting (#2018)
Format floats the same way as actions/runner (precision 15, remove
trailing zeroes)

See: 67d70803a9/src/Sdk/DTObjectTemplating/ObjectTemplating/Tokens/NumberToken.cs (L34)
2023-10-13 20:01:04 +00:00
Lunny Xiao
c93462e19f Merge pull request 'bump nektos to 0.2.52' (#79) from bump-nektos into main
Reviewed-on: https://gitea.com/gitea/act/pulls/79
Reviewed-by: John Olheiser <john+gitea@jolheiser.com>
2023-10-13 01:20:21 +00:00
dependabot[bot]
99067a9c1e build(deps): bump golang.org/x/net from 0.15.0 to 0.17.0 (#2045)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.15.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.15.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-11 23:30:36 +00:00
techknowlogick
f3264cac20 Merge remote-tracking branch 'upstream/master' into bump-nektos 2023-10-11 15:28:38 -04:00
dependabot[bot]
e7e158cd7e build(deps): bump github.com/docker/distribution (#2037)
Bumps [github.com/docker/distribution](https://github.com/docker/distribution) from 2.8.2+incompatible to 2.8.3+incompatible.
- [Release notes](https://github.com/docker/distribution/releases)
- [Commits](https://github.com/docker/distribution/compare/v2.8.2...v2.8.3)

---
updated-dependencies:
- dependency-name: github.com/docker/distribution
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-10-09 02:52:56 +00:00
dependabot[bot]
3c730d7924 build(deps): bump golang.org/x/term from 0.12.0 to 0.13.0 (#2036)
Bumps [golang.org/x/term](https://github.com/golang/term) from 0.12.0 to 0.13.0.
- [Commits](https://github.com/golang/term/compare/v0.12.0...v0.13.0)

---
updated-dependencies:
- dependency-name: golang.org/x/term
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-09 02:35:38 +00:00
ChristopherHX
976df8bae5 fix action_ref (composite action) (#2020)
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-10-03 23:13:05 +00:00
ChristopherHX
7c7d80ebdd fix: use actions/runner hashfiles in container (#1940)
* fix: use actions/runner hashfiles in container

Previously hashfiles ran on the host,
this don't work for container generated content

* fix: lint

* fix: lint

* fix assign follow symlink flag

Co-authored-by: Jason Song <i@wolfogre.com>

---------

Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-10-03 22:56:18 +00:00
ChristopherHX
2f479ba024 Fix image survey for large images (#2022)
ubuntu 22.04 based large image is now available

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-10-03 22:39:49 +00:00
Diego Sousa
5718555f7a [ Variables ] - Add missing documentation for repository variables (#2032)
* docs: 📝 add vars instructions by cli and file loading

* docs: 📝 adjust varibles instructions
2023-10-03 22:02:22 +00:00
vilunov
4c35288175 Add DIND docker-compose example (#365)
Co-authored-by: Nikita Vilunov <nikita@vilunov.me>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/365
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: vilunov <vilunov@noreply.gitea.com>
Co-committed-by: vilunov <vilunov@noreply.gitea.com>
2023-10-02 15:12:31 +00:00
Andrew Imeson
990db1bfc0 Fix k8s rootless Docker filesystem permissions (#366)
Without this the deployment fails because the runner can't write to
/data/

Credit to @ccureau for identifying the fix

Relates to: gitea/act_runner#264

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/366
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: Andrew Imeson <andrew@andrewimeson.com>
Co-committed-by: Andrew Imeson <andrew@andrewimeson.com>
2023-10-02 15:12:14 +00:00
GitHub Actions
44ea01c209 chore: bump VERSION to 0.2.52 2023-10-01 02:15:05 +00:00
dependabot[bot]
2be4def7be build(deps): bump github.com/rhysd/actionlint from 1.6.25 to 1.6.26 (#2026)
Bumps [github.com/rhysd/actionlint](https://github.com/rhysd/actionlint) from 1.6.25 to 1.6.26.
- [Release notes](https://github.com/rhysd/actionlint/releases)
- [Changelog](https://github.com/rhysd/actionlint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rhysd/actionlint/compare/v1.6.25...v1.6.26)

---
updated-dependencies:
- dependency-name: github.com/rhysd/actionlint
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2023-09-25 03:24:27 +00:00
dependabot[bot]
3d47885894 build(deps): bump megalinter/megalinter from 7.3.0 to 7.4.0 (#2025)
Bumps [megalinter/megalinter](https://github.com/megalinter/megalinter) from 7.3.0 to 7.4.0.
- [Release notes](https://github.com/megalinter/megalinter/releases)
- [Changelog](https://github.com/oxsecurity/megalinter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/megalinter/megalinter/compare/v7.3.0...v7.4.0)

---
updated-dependencies:
- dependency-name: megalinter/megalinter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-25 03:06:54 +00:00
techknowlogick
d07fbfc8c3 bump act library to v0.2.51 (#360)
brings in node20 support

Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/360
Reviewed-by: delvh <dev.lh@web.de>
2023-09-24 19:20:40 +00:00
m90
10dc6fb60d Allow reading registration token from file (#350)
When deploying the runner in a Docker Swarm setup, it can be useful to
read the potentially sensitive token from a secret instead of having to
pass it from an environment variable.

Co-authored-by: Frederik Ring <frederik.ring@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/350
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: m90 <m90@noreply.gitea.com>
Co-committed-by: m90 <m90@noreply.gitea.com>
2023-09-04 04:12:07 +00:00
TheFox0x7
ed35b09b8f change podman socket path (#341)
port of https://github.com/nektos/act/pull/1961
closes gitea/act_runner#274

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/341
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: TheFox0x7 <thefox0x7@gmail.com>
Co-committed-by: TheFox0x7 <thefox0x7@gmail.com>
2023-08-21 04:01:12 +00:00
Gianni Angelozzi
03f0829d09 Add ForcePull option (#339)
Close #271

What it does: instead of forcing the value of `ForcePull` to false, the user can now configure it on the runner yaml

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/339
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Gianni Angelozzi <gianni@sistema3.it>
Co-committed-by: Gianni Angelozzi <gianni@sistema3.it>
2023-08-17 06:51:57 +00:00
Jason Song
7fc1b91ba6 Update to go1.21 in Dockerfile.rootless (#332)
Follow #330

Fix https://gitea.com/gitea/act_runner/pulls/330#issuecomment-747099

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/332
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: appleboy <appleboy.tw@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2023-08-12 00:37:08 +00:00
harryzcy
82c3c2df1a Upgrade Go to 1.21 and bump other dependencies (#330)
Co-authored-by: harryzcy <harry@harryzheng.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/330
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: harryzcy <harryzcy@noreply.gitea.com>
Co-committed-by: harryzcy <harryzcy@noreply.gitea.com>
2023-08-10 01:45:25 +00:00
sillyguodong
9fc823e4b1 Fix "http: invalid Host header" issue (#319)
This issue  is caused by the addition of validation logic for `Host` in `http.Request` in golang 1.20.6 (see https://go-review.googlesource.com/c/go/+/507357/6/src/net/http/request.go)

In `act`, when execute `ContainerExecAttach()`(see 22d91e3ac3/pkg/container/docker_run.go (L594)), the actual value of `request.Host` is `"/var/run/docker.sock"`. This does not conform to the specification described in `validhostHeader`.
 <details> <summary>ValidHostHeader()</summary>

![image](/attachments/57fb13ba-1c74-47f6-ac48-231a72a1947e)
</details>
So this PR follow upstream: https://github.com/nektos/act/pull/1917 and revert https://gitea.com/gitea/act_runner/pulls/295

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/319
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-08-02 04:34:36 +00:00
sillyguodong
12999b61dd Reduce unnecessary DB queries for Actions tasks (#219)
implement: https://github.com/go-gitea/gitea/issues/24544

Changes:
- Add a global variable `tasksVersion` to store the lastest version number which returned by Gitea.
- Pass `tasksVersion` to Gitea when invoking `FetchTask`.
- If there is no task in the `FetchTask` response, store the version from the `FetchTask` response into `tasksVersion` variable.
- If there is a task in the `FetchTask` response, set `tasksVersion` to zero to focre query db in next `FetchTask` request.

Related:
- Protocol: https://gitea.com/gitea/actions-proto-def/pulls/10
- Gitea side: https://github.com/go-gitea/gitea/pull/25199

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/219
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-07-25 03:25:50 +00:00
caicandong
49a2fcc138 fix endless loop (#306)
fix endless loop in  poll
relate #305

Co-authored-by: CaiCandong <1290147055@qq.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/306
Co-authored-by: caicandong <caicandong@noreply.gitea.com>
Co-committed-by: caicandong <caicandong@noreply.gitea.com>
2023-07-24 07:07:53 +00:00
Konstantin Podsvirov
8f88e4f15a Update README.md (#302)
Correct Quick Start section.

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/302
Co-authored-by: Konstantin Podsvirov <podsvirov@noreply.gitea.com>
Co-committed-by: Konstantin Podsvirov <podsvirov@noreply.gitea.com>
2023-07-24 05:11:42 +00:00
caicandong
a1bb3b56fd Catch the panic and print the error (#305)
refactor # 215  Catch the panic and print the error
close #215

Co-authored-by: CaiCandong <1290147055@qq.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/305
Co-authored-by: caicandong <caicandong@noreply.gitea.com>
Co-committed-by: caicandong <caicandong@noreply.gitea.com>
2023-07-24 04:28:44 +00:00
sillyguodong
1a7ec5f339 Upgrade gitea/act (#298)
Follow  https://gitea.com/gitea/act/pulls/75

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/298
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-07-20 05:12:59 +00:00
Jason Song
5d01cb8904 Add tips in config file (#297)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/297
2023-07-20 02:16:30 +00:00
Thomas E Lackey
dcf84d8a53 Avoid https://github.com/nektos/act/issues/1908 by specifying golang 1.20.5. (#295)
Without this, actions fail when attempting to clone the repo:

```
2023-07-18 17:11:02 [Smoke Test/Run basic test suite] failed to attach to exec: http: invalid Host header
```

This appears to be the same as https://github.com/nektos/act/issues/1908, and only shows up with golang 1.20.6.  Staying with golang 1.20.5 is a temporary solution to avoid the bug until it is fixed upstream.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/295
Co-authored-by: Thomas E Lackey <telackey@bozemanpass.com>
Co-committed-by: Thomas E Lackey <telackey@bozemanpass.com>
2023-07-19 05:12:07 +00:00
Zettat123
73adae040d Upgrade act (#291)
Follow https://gitea.com/gitea/act/pulls/74
Fix #277, #290

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/291
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-07-17 03:44:42 +00:00
Bo-Yi Wu
db662b3690 ci(lint): refactor code for clarity and linting compliance (#289)
- Removed `deadcode`, `structcheck`, and `varcheck` linters from `.golangci.yml`
- Fixed a typo in a comment in `daemon.go`
- Renamed `defaultActionsUrl` to `defaultActionsURL` in `exec.go`
- Removed unnecessary else clause in `exec.go` and `runner.go`
- Simplified variable initialization in `exec.go`
- Changed function name from `getHttpClient` to `getHTTPClient` in `http.go`
- Removed unnecessary else clause in `labels_test.go`

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/289
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2023-07-13 01:10:54 +00:00
Bo-Yi Wu
cf92a979e2 refactor(register): refactor registration functions and improve logging (#288)
- Remove the else clause in the `registerInteractive` function, and unconditionally log "Runner registered successfully."
- Change the return value in the `doRegister` function from `nil` to `ctx.Err()`.

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/288
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2023-07-12 14:12:16 +00:00
Bo-Yi Wu
87058716fb fix(register): refactor context usage in registration functions (#286)
- Add context parameter to `registerNoInteractive`, `registerInteractive`, and `doRegister` functions
- Remove the creation of a new context in `doRegister`, now using the passed context instead

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/286
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2023-07-12 13:11:55 +00:00
Lunny Xiao
c701ba4787 Add a quick start runner method in README (#282)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/282
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-07-12 01:43:26 +00:00
Michael Santos
57ff1df6e0 config: default container workspace set to host path (#279)
The container workspace path is overwritten by the default host workspace path ($HOME/.cache/act).

Workaround:
```yaml
host:
  workdir_parent: workspace
```

Ref: 34d15f21c2
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/279
Co-authored-by: Michael Santos <michael.santos@gmail.com>
Co-committed-by: Michael Santos <michael.santos@gmail.com>
2023-07-10 08:57:55 +00:00
Zettat123
3dcfd6ea3d Run as cache server (#275)
This PR
- adds the `cache-server` command so act_runner can run as a cache server. When running as a cache server, act_runner only processes the requests related to cache and does not run jobs.
- adds the `external_server` configuration for cache. If specified, act_runner will use this URL as the ACTIONS_CACHE_URL instead of starting a cache server itself.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/275
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-07-07 08:28:54 +00:00
Zettat123
c6006ee699 Upgrade act (#269)
Follow https://gitea.com/gitea/act/pulls/71.
Fix https://gitea.com/gitea/act_runner/issues/266

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/269
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-07-03 04:15:46 +00:00
Zettat123
f2629f2ea3 Add support for finding docker daemon from common socket paths (#263)
Caused by #260

act_runner will fail to start if user does not set `docker_host` configuration and `DOCKER_HOST` env. This PR adds the support for finding docker daemon from common socket paths so act_runner could detect the docker socket from these paths.

The `commonSocketPaths` is from [nektos/act](e60018a6d9/cmd/root.go (L124-L131))

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/263
Co-authored-by: Zettat123 <zettat123@noreply.gitea.com>
Co-committed-by: Zettat123 <zettat123@noreply.gitea.com>
2023-07-01 01:27:54 +00:00
Jason Song
cf48ed88ba Revert supporting multiple default actions URLs and use github for exec by default (#262)
## ⚠️ BREAKING ⚠️

Follow https://github.com/go-gitea/gitea/pull/25581 and gitea/act#70 .

- Revert "Parse multiple default actions URLs (#200)"
- Revert "fix defaultActionsUrls config for exec (#233)"
- Use `https://github.com` for exec by default.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/262
2023-06-30 07:53:18 +00:00
Zettat123
ccc27329dc Improve the usage of docker_host configuration (#260)
Follow #242, #244
Fixes #258

Users could use `docker_host` configuration to specify which docker daemon will be used by act_runner.
- If `docker_host` is **empty**, act_runner will find an available docker host automatically.
- If `docker_host` is **"-"**, act_runner will find an available docker host automatically, but the docker host won't be mounted to the job containers and service containers.
- If `docker_host` is **not empty or "-"**, the specified docker host will be used. An error will be returned if it doesn't work.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/260
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-06-30 04:00:04 +00:00
Tomasz Duda
b0bd503b11 add token support for exec (#247)
allow to pass token from secrets

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/247
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Tomasz Duda <tomaszduda23@gmail.com>
Co-committed-by: Tomasz Duda <tomaszduda23@gmail.com>
2023-06-20 08:41:22 +00:00
Zettat123
8c14933e70 Upgrade act (#248)
Follow https://gitea.com/gitea/act/pulls/68

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/248
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-06-20 08:33:42 +00:00
Marius Zwicker
34d15f21c2 Add option to configure workspace on host (#238)
Adds a new section to the configuration which is used
to control options when running in host mode.

The first option added is to allow configuration
of the location workspaces get created in.

Depends on ~~gitea/act#65~~
Will resolve #235

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/238
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Marius Zwicker <marius.zwicker@mlba-team.de>
Co-committed-by: Marius Zwicker <marius.zwicker@mlba-team.de>
2023-06-20 08:29:05 +00:00
Tomasz Duda
32d29f0813 add ACT_EXEC (#246)
Add env variable to distinguish build run locally from remote one.

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/246
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Tomasz Duda <tomaszduda23@gmail.com>
Co-committed-by: Tomasz Duda <tomaszduda23@gmail.com>
2023-06-20 02:08:35 +00:00
Tomasz Duda
2e2c0400c8 add --gitea-instance (#245)
add --gitea-instance to let user specify address of endpoint of exec
Related to https://gitea.com/gitea/act/pulls/68. Both can be merged independently though.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/245
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Tomasz Duda <tomaszduda23@gmail.com>
Co-committed-by: Tomasz Duda <tomaszduda23@gmail.com>
2023-06-20 01:57:21 +00:00
Jason Song
054c8d912f Move docker.host to container.docker_host (#244)
Follow #242.

Move `docker.host` to `container.docker_host`.

There are already some options for docker/container in `container`, so developers could get confused about where to add options.

It's breaking, but I think it's OK since `docker.host` was added just two days ago.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/244
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-06-19 09:01:16 +00:00
appleboy
9e4a5f7363 feat: improve Docker configuration and detection handling (#242)
- Pass `cfg` to `envcheck.CheckIfDockerRunning` function
- Add `Docker` struct to `config.go` for Docker configuration
- Update `config.example.yaml` with `docker` configuration options
- Modify `CheckIfDockerRunning` in `docker.go` to use Docker host from config if provided

Signed-off-by: appleboy <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/242
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: wxiaoguang <wxiaoguang@noreply.gitea.com>
Co-authored-by: appleboy <appleboy.tw@gmail.com>
Co-committed-by: appleboy <appleboy.tw@gmail.com>
2023-06-18 05:38:38 +00:00
Zettat123
ec38401097 Add ValidVolumes config (#226)
Follow https://gitea.com/gitea/act/pulls/60, https://gitea.com/gitea/act/pulls/64

This PR adds the `valid_volumes` configuration. `valid_volumes` is a sequence containing the volumes (including bind mounts) that can be mounted to the container. By default, `valid_volumes` is empty, which means that no volumes can be mounted. Users can specify multiple valid volumes and [glob](https://github.com/gobwas/glob) is supported.

All volumes will be allowed when using `exec` to run workflows locally.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/226
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-06-16 06:07:48 +00:00
Jason Song
45bfe0a9b2 Accept empty file as config (#241)
Close #240

`yaml.Decoder.Decode` will return EOF when the root node is nil , see https://github.com/go-yaml/yaml/blob/v3/yaml.go#L125

While `yaml.Unmarshal` will accept it, see https://github.com/go-yaml/yaml/blob/v3/yaml.go#L162

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/241
Reviewed-by: Zettat123 <zettat123@noreply.gitea.com>
2023-06-15 05:40:37 +00:00
Jason Song
316534996a Build docker image gitea/act_runner/x.y.z-dind-rootless (#239)
Follow #208

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/239
Reviewed-by: Zettat123 <zettat123@noreply.gitea.com>
2023-06-15 05:27:35 +00:00
sillyguodong
67b1363d25 Support changing labels (#201)
Implement proposal: https://github.com/go-gitea/gitea/issues/24540

Related:
- Protocol: https://gitea.com/gitea/actions-proto-def/pulls/9
- Gitea side: https://github.com/go-gitea/gitea/pull/24806

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/201
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-06-15 03:59:15 +00:00
Tomasz Duda
946c41cf4f Improve run.sh to handle empty labels and log to stdout (#237)
1. Print logs on standard output
2. Don't add labels if GITEA_RUNNER_LABELS not set

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/237
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Tomasz Duda <tomaszduda23@gmail.com>
Co-committed-by: Tomasz Duda <tomaszduda23@gmail.com>
2023-06-13 04:09:11 +00:00
ccureau
341d49a24d implement act_runner rootless image (#208)
This PR creates a rootless Docker image that runs both `dockerd` and `act_runner` using `supervisord`.  It has been tested locally for a few days and seems stable.

Co-authored-by: ccureau <ccureau@noreply.gitea.io>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/208
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: ccureau <ccureau@noreply.gitea.com>
Co-committed-by: ccureau <ccureau@noreply.gitea.com>
2023-06-12 06:35:27 +00:00
MarkusLoeffler01
b21d476aca Exit with Code 1 if registering a runner fails (#228)
### It's a "simple dirty fix" and I don't have any experiences with Go, so if this doesn't match your coding compliance, please adjust the code as needed

I'm using bash scripts to register a token

`./act_runner/act_runner register --no-interactive --name runner$number --instance http://localhost:3000 --token $token`

But when a token is invalid, the command still returns 0, which is not practical for automation.
A simple non-zero return would be more convenient for power users and developers.

Co-authored-by: Markus Löffler <markus.loeffler@netcare.de>
Co-authored-by: techknowlogick <techknowlogick@noreply.gitea.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/228
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Co-authored-by: MarkusLoeffler01 <markusloeffler01@noreply.gitea.com>
Co-committed-by: MarkusLoeffler01 <markusloeffler01@noreply.gitea.com>
2023-06-09 17:34:23 +00:00
Jason Song
a29307a9d9 Remove hadolint and improve Dockerfile (#234)
Replace #190

See:

- https://gitea.com/gitea/act_runner/pulls/190#issuecomment-741196
- https://gitea.com/gitea/act_runner/pulls/208#issuecomment-741049

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/234
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: silverwind <silverwind@noreply.gitea.com>
Reviewed-by: delvh <dev.lh@web.de>
2023-06-09 02:50:30 +00:00
a1012112796
4bfbfec477 fix defaultActionsUrls config for exec (#233)
follow #200

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/233
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: a1012112796 <1012112796@qq.com>
Co-committed-by: a1012112796 <1012112796@qq.com>
2023-06-08 04:26:52 +00:00
Zettat123
fed01c9807 Parse multiple default actions URLs (#200)
Follow https://gitea.com/gitea/act/pulls/58
Resolve https://github.com/go-gitea/gitea/issues/24789

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/200
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-06-06 04:03:02 +00:00
Bo-Yi Wu
a83f29d5a9 docs: improve examples README and organization (#230)
- Update the introduction and descriptions in the examples README
- Add a table with descriptions for each section (docker, docker-compose, kubernetes, vm)

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/230
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2023-06-06 02:42:58 +00:00
Bo-Yi Wu
69c55ee003 refactor: daemon, config, and logging for better clarity (#225)
- Import "path", "runtime", "strconv", and "strings" packages in daemon.go
- Move "Starting runner daemon" log message to a different location
- Refactor log formatter initialization and add debug level caller information
- Split Config struct into separate Log, Runner, Cache, and Container structs with comments in config.go

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/225
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2023-06-05 13:11:23 +00:00
a1012112796
01ef57c667 fix artifactServerPath and artifactServerAddr config for exec (#221)
fix exec logic to make `actions/download-artifact` and `actions/upload-artifact` can be used.

example result:

```YML
name: test-artifact
on:
  - push
  - pull_request

jobs:
  test-artifact-1:
    name: test 1
    runs-on: ubuntu-latest
    steps:
      - run: echo `date` | tee time.txt
      - name: cache build result
        uses: actions/upload-artifact@v3
        with:
          name: build-artifact
          path: time.txt
          retention-days: 1

  test-artifact-2:
    name: test 2
    needs: test-artifact-1
    runs-on: ubuntu-latest
    steps:
      - name: Retrieve saved build result
        uses: actions/download-artifact@v3
        with:
          name: build-artifact
          path: .
      - run: ls -lh
      - run: cat time.txt

```

![image](/attachments/5cad3b4a-930a-4d42-a1ae-45ac32e6bfc2)

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/221
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: a1012112796 <1012112796@qq.com>
Co-committed-by: a1012112796 <1012112796@qq.com>
2023-06-05 08:51:44 +00:00
Chris Cureau
a384adbbc6 Documentation enhancements (#207)
This PR addresses the issue listed in issue #170 regarding how to set up rootless Docker. It also expands on the documentation to show how to create deployments for different environments.

Co-authored-by: ccureau <ccureau@noreply.gitea.io>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/207
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Chris Cureau <cmcureau@gmail.com>
Co-committed-by: Chris Cureau <cmcureau@gmail.com>
2023-06-05 08:46:15 +00:00
harryzcy
e3271d8469 Remove trailing slash from instance address (#197)
Related #136

Co-authored-by: harryzcy <harry@harryzheng.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/197
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: harryzcy <harryzcy@noreply.gitea.io>
Co-committed-by: harryzcy <harryzcy@noreply.gitea.io>
2023-05-22 23:50:29 +08:00
sillyguodong
84386c1b16 Add exec command flag of network (#192)
Related to #184
Add command flag of `network` for `exec`, the default value of `--network` is empty string. Valid values are: `host `, `bridge`, `<custom_network>` and empty string.

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/192
Reviewed-by: a1012112796 <1012112796@qq.com>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-05-18 15:01:43 +08:00
ChristopherHX
fd7c8580af Prevent exposing GITEA_RUNNER_REGISTRATION_TOKEN to act (#188)
You can currently expose the token to jobs even while using docker in docker

`-e GITEA_RUNNER_REGISTRATION_TOKEN` tells the docker client of act to read GITEA_RUNNER_REGISTRATION_TOKEN from the process and now it can be stolen.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/188
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: ChristopherHX <christopherhx@noreply.gitea.io>
Co-committed-by: ChristopherHX <christopherhx@noreply.gitea.io>
2023-05-17 14:13:38 +08:00
sillyguodong
35596a182b Add configuration item of container.network (#184)
Close https://gitea.com/gitea/act_runner/issues/177
Related https://gitea.com/gitea/act/pulls/56

### ⚠️ Breaking
The `container.network_mode` is a deprecated configuration item. It may be removed after Gitea 1.20 released.
Previously, if the value of `container.network_mode` is `bridge`, it means that `act_runner` will create a new network for job.But `bridge` is easily confused with the bridge network created by Docker by default.
We recommand that using `container.network` to specify the network to which containers created by `act_runner` connect.

###  🆕 container.network
The configuration file of `act_runner` add a new item of `contianer.network`.
In `config.example.yaml`:
```yaml
container:
  # Specifies the network to which the container will connect.
  # Could be host, bridge or the name of a custom network.
  # If it's empty, act_runner will create a network automatically.
  network: ""
```

As the comment in the example above says, the purpose of the `container.network` is specifying the network to which containers created by `act_runner` will connect.

`container.network` accepts the following valid values:
- `host`: All of containers (including job containers and service contianers) created by `act_runner` will be connected to the network named `host` which is created automatically by Docker. Containers will share the host’s network stack and all interfaces from the host will be available to these containers.
- `bridge`: It is similar to `host`. All of containers created by `act_runner` will be connected to the network named `bridge` which is created automatically by Docker. All containers connected to the `bridge` (Perhaps there are containers that are not created by `act_runner`) are allowed to communicate with each other, while providing isolation from containers which are not connected to that `bridge` network.
- `<custom_network>`: Please make sure that the `<custom_network>` network already exists firstly (`act_runner` does not detect whether the specified network exists currently. If not exists yet, will return error in the stage of `docker create`). All of containers created by `act_runner` will be connected to `<custom_network>`. After the job is executed, containers are removed and automatically disconnected from the `<custom_network>`.
- empty: `act_runner` will create a new network for each job container and their service containers (if defined in workflow). So each job container and their service containers share a network environment, but are isolated from others container and the Docker host. Of course, these networks created by `act_runner` will be removed at last.

### Others
- If you do not have special needs, we highly recommend that setting `container.network` to empty string (and do not use `container.network_mode` any more). Because the containers created by `act_runner` will connect to the networks that are created by itself. This point will provide better isolation.
- If you set `contianer.network` to empty string or `<custom_network>`, we can be access to service containers by `<service-id>:<port>` in the steps of job. Because we added an alias to the service container when connecting to the network.

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/184
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-05-16 14:46:59 +08:00
silverwind
c9d3f67264 Add .editorconfig and .gitattributes (#186)
Add some files that belong in every repo.

- `.editorconfig` is based on `gitea` repo.
- `.gitattributes` is useful for Windows users.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/186
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: delvh <dev.lh@web.de>
Co-authored-by: silverwind <me@silverwind.io>
Co-committed-by: silverwind <me@silverwind.io>
2023-05-13 23:51:22 +08:00
Alex Lau (AvengerMoJo)
94031fc198 Fix README.md typo on daemon (#183)
It is just a typo fix.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/183
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Alex Lau (AvengerMoJo) <avengermojo@gmail.com>
Co-committed-by: Alex Lau (AvengerMoJo) <avengermojo@gmail.com>
2023-05-11 14:25:39 +08:00
sillyguodong
d5e4baed54 Fix missing runner version when building images from a Dockerfile (#181)
regression of https://gitea.com/gitea/act_runner/pulls/172

https://gitea.com/gitea/act_runner/actions/runs/400/jobs/1
In the step of `Build and push`, log output that `git: not found`. This lead to runner's version not being injected when go compile.
![image](/attachments/031ecdf2-6baa-410c-bab7-c6945140c5ad)

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/181
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-05-09 16:44:31 +08:00
sando38
d4caa7e065 Dockerfile: Improve signal handling by adding a runtime init (#180)
This adds tini as a runtime init (https://github.com/krallin/tini). It improves signal handling for the container, see https://github.com/krallin/tini#why-tini.

An alternative could be to run the container with `docker run --init ...` which also places tini as a runtime init as PID 1.

Co-authored-by: sando38 <sandomir@tutanota.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/180
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sando38 <sando38@noreply.gitea.io>
Co-committed-by: sando38 <sando38@noreply.gitea.io>
2023-05-09 16:09:48 +08:00
Jason Song
de4160b023 Skip counting log length when parseLogRow return nil (#176)
Fix:
![image](/attachments/93e29bc0-3599-4f7e-8b90-512562a5d711)

Regression of #149, `LogLength` could be incorrect.

It may be related to https://github.com/go-gitea/gitea/issues/24458

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/176
Reviewed-by: Zettat123 <zettat123@noreply.gitea.io>
Reviewed-by: wxiaoguang <wxiaoguang@noreply.gitea.io>
2023-05-06 17:00:52 +08:00
a1012112796
609c0a0773 fix --event option logic for exec (#175)
- fix `--event` option logic
- by the way, apply a `TODO` logic

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/175
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: a1012112796 <1012112796@qq.com>
Co-committed-by: a1012112796 <1012112796@qq.com>
2023-05-06 11:27:08 +08:00
Jason Song
0c029f7e79 Upgrade act and use new artifactcache (#174)
- Upgrade act to v0.245.1
- Replace `gitea.com/gitea/act_runner/internal/app/artifactcache` with `github.com/nektos/act/pkg/artifactcache`

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/174
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-05-04 18:45:01 +08:00
Bo-Yi Wu
eef3c32eb2 ci: improve release process and test robustness (#173)
- Add `.xz` and `.xz.sha256` files to the release extra files in `.goreleaser.yaml`

upload `.xz` and `.xz.sha256` to Gitea release page.

![image](/attachments/0218072d-c235-461a-8f52-969aeb6e5b07)

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/173
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2023-05-04 09:41:22 +08:00
Bo-Yi Wu
c40b651873 chore: improve Dockerfile, README, and testing settings (#172)
- Remove `git=2.38.5-r0` from the `apk add` command in Dockerfile
- Update the download link for act_runner in README.md to be a clickable link

Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/172
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Bo-Yi Wu <appleboy.tw@gmail.com>
Co-committed-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2023-05-04 09:36:31 +08:00
appleboy
b498341857 build: improve compression and update GitHub actions (#168)
- Add `dist` to .gitignore for gorelease binary folder
- Replace tar command with xz command in .goreleaser.yaml for better compression

ref: https://gitea.com/gitea/homebrew-gitea/pulls/164

Signed-off-by: appleboy <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/168
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Co-authored-by: appleboy <appleboy.tw@gmail.com>
Co-committed-by: appleboy <appleboy.tw@gmail.com>
2023-05-01 21:59:43 +08:00
appleboy
0d727eb262 build: optimize Dockerfile and update dependencies (#162)
- Update base images to golang:1.20-alpine3.17 and alpine:3.17
- Replace `--update-cache` with `--no-cache` in apk add command
- Specify exact versions for make, git, and bash packages

Signed-off-by: appleboy <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/162
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: appleboy <appleboy.tw@gmail.com>
Co-committed-by: appleboy <appleboy.tw@gmail.com>
2023-04-29 12:07:15 +08:00
vbrandl
7c71c94366 Document persisting /data for docker container (#160)
`/data` must be kept between container restarts.

Co-authored-by: Valentin Brandl <mail@vbrandl.net>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/160
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Co-authored-by: vbrandl <vbrandl@noreply.gitea.io>
Co-committed-by: vbrandl <vbrandl@noreply.gitea.io>
2023-04-29 03:05:00 +08:00
appleboy
49d2cb0cb5 ci: improve API usage and test robustness across platforms (#159)
- Add `dir: ./dist/` to `.goreleaser.yaml` builds configuration

fix https://gitea.com/gitea/act_runner/issues/158

Signed-off-by: appleboy <appleboy.tw@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/159
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: appleboy <appleboy.tw@gmail.com>
Co-committed-by: appleboy <appleboy.tw@gmail.com>
2023-04-28 23:46:46 +08:00
sillyguodong
85626b6bbd Support configuration variables (#157)
related to: https://gitea.com/gitea/act_runner/issues/127

`act_runner` only needs to pass `vars` from `Gitea` to `act`.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/157
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-04-28 22:06:08 +08:00
Zettat123
35400f76fa Add parent directory for working directory (#154)
Fixes #145

At present, the working directory of a work flow is a path like `/<owner>/<repo>`, so the directory may conflict with system directory like `/usr/bin`. We need to add a parent directory for the working directory.
In this PR, the parent directory is `/workspace` by default and users could configure it by the `workdir_parent` option.

This change doesn't affect the host mode because in host mode the working directory will always be in `$HOME/.cache/act/` directory.

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/154
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-04-28 22:03:52 +08:00
Jason Song
0cf31b2d22 Update docker image tag (#153)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/153
2023-04-27 15:02:39 +08:00
Søren L. Hansen
c8cc7b2448 Workflow commands (#149)
Establishes a simple framework for supporting workflow commands.

Fully implements `::add-mask::`, `::debug::`, and `::stop-commands::`.

Addresses #148

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/149
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Søren L. Hansen <sorenisanerd@gmail.com>
Co-committed-by: Søren L. Hansen <sorenisanerd@gmail.com>
2023-04-27 12:32:48 +08:00
Jason Song
3be962cdb3 Rename the download folder from main -> nightly (#152)
Close https://gitea.com/gitea/act_runner/issues/117

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/152
2023-04-27 12:23:15 +08:00
Jason Song
a5edbc9ac4 Release docker images (#151)
Did some tests to make sure it worked.

See https://hub.docker.com/r/gitea/act_runner/tags

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/151
2023-04-27 12:08:41 +08:00
seepine
66bab3d805 ci(actions): add build docker image workflow (#118)
### Add secret
```
DOCKERHUB_TOKEN=xxx
```

### Tag
when tag like `v1.0.0`, it will build multi platform docker image `gitea/act_runner:1.0.0` and `gitea/act_runner:latest`, then push to docker hub

### Use
> volume `/data` save `.runner` config file
> volume `/root/.cache` save actcache and actions cache

```sh
docker run -e GITEA_INSTANCE_URL=***                    \
           -e GITEA_RUNNER_REGISTRATION_TOKEN=***       \
           -e GITEA_RUNNER_NAME=***                     \
           -v /var/run/docker.sock:/var/run/docker.sock \
           -v /root/act_runner/data:/data               \
           -v /root/act_runner/cache:/root/.cache       \
           gitea/act_runner
```
Test join runners success
![image](/attachments/f5287e93-e27c-420f-a3d5-8f9b54bfdbb6)
Test run action success
![image](/attachments/7235af17-f598-4fc8-88b4-d4771b1f07cd)

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/118
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: seepine <seepine@noreply.gitea.io>
Co-committed-by: seepine <seepine@noreply.gitea.io>
2023-04-27 11:32:28 +08:00
techknowlogick
293926f5d5 bump modernc.org/sqlite (#141)
fix #140

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/141
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-committed-by: techknowlogick <techknowlogick@gitea.io>
2023-04-25 03:16:01 +08:00
You-Sheng Yang
43c5ba923f make: skip --disable-content-trust at docker buildx (#139)
`docker build` may be aliased as `docker buildx build`, which doesn't support --disable-content-trust switch.

Signed-off-by: You-Sheng Yang <vicamo@gmail.com>

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/139
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Co-authored-by: You-Sheng Yang <vicamo@gmail.com>
Co-committed-by: You-Sheng Yang <vicamo@gmail.com>
2023-04-25 03:15:48 +08:00
techknowlogick
acc5afc428 allow building act_runner with cgo (#137)
for platforms not supported by moderc.org/sqlite

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/137
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: techknowlogick <techknowlogick@gitea.io>
Co-committed-by: techknowlogick <techknowlogick@gitea.io>
2023-04-24 10:45:51 +08:00
Zettat123
27a1a90d25 Upgrade act (#135)
Related to:

- https://gitea.com/gitea/act/pulls/45
- https://gitea.com/gitea/act/pulls/47

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/135
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-04-21 10:48:26 +08:00
Jason Song
83ec0ba909 Support upload outputs and use needs context (#133)
See [Example usage of the needs context](https://docs.github.com/en/actions/learn-github-actions/contexts#example-usage-of-the-needs-context).

Related to:
- [actions-proto-def #5](https://gitea.com/gitea/actions-proto-def/pulls/5)
- [gitea #24230](https://github.com/go-gitea/gitea/pull/24230)

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/133
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Zettat123 <zettat123@noreply.gitea.io>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2023-04-20 23:27:46 +08:00
Jason Song
ed86e2f15a Update workflow files (#131)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/131
2023-04-19 17:46:52 +08:00
Jason Song
d4bebccc12 Update dependencies (#130)
Related to:
- https://gitea.com/gitea/act/pulls/44
- https://gitea.com/gitea/actions-proto-def/pulls/5
- https://gitea.com/gitea/actions-proto-def/pulls/7

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/130
2023-04-19 16:50:07 +08:00
Zettat123
c75b67e892 Upgrade act (#128)
Follow https://gitea.com/gitea/act/pulls/42

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/128
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-04-19 14:53:23 +08:00
Zettat123
bc6031eff7 Fix reporting log in Reporter.Close (#126)
Previously, the `Close` func returns incorrectly so that the logs may not be reported.

This PR fixes the incorrect return and sets the `StoppedAt` to get the correct task duration.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/126
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-04-19 11:17:49 +08:00
Zettat123
c69c353d93 Upgrade act (#124)
Related to https://gitea.com/gitea/act/pulls/40

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/124
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-04-14 18:20:04 +08:00
Lunny Xiao
fcc016e9b3 Special the release tag signing sub key (#121)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/121
2023-04-13 18:56:15 +08:00
Jason Song
d5caee38f2 Add prefix to use ghaction-import-gpg (#120)
See https://gitea.com/gitea/act_runner/actions/runs/254

Related to #116

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/120
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2023-04-13 18:18:39 +08:00
Lunny Xiao
9e26208e13 Add signature for tag releases, upload to gitea releases itself (#116)
GPG signature keys are not set yet.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/116
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-04-13 16:49:50 +08:00
Lunny Xiao
a05c5ba3ad Add make docker (#115)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/115
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-04-13 04:17:08 +08:00
Jason Song
c248520a66 Set specific environments to distinguish between Gitea and GitHub (#113)
Close https://github.com/go-gitea/gitea/issues/24038

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/113
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2023-04-12 14:44:26 +08:00
Lunny Xiao
10d639cc6b add release tag (#111)
Fix #108

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/111
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-04-11 14:04:08 +08:00
Thomas E Lackey
5a8134410d Run as a container (#8) including Docker-in-Docker. (#84)
This adds a very simple Dockerfile and run script for running `act_runner` as a container.

It also allows setting `Privileged` and `ContainerOptions` flags via the new config file when spawning task containers.  The combination makes it possible to use Docker-in-Docker (which requires `privileged` mode) as well as pass any other options child Docker containers may require.

For example, if Gitea is running in Docker on the same machine, for the `checkout` action to behave as expected from a task container launched by `act_runner`, it might be necessary to map the hostname via something like:

```
container:
  network_mode: bridge
  privileged: true
  options: --add-host=my.gitea.hostname:host-gateway
```

> NOTE: Description updated to reflect latest code.
> NOTE: Description updated to reflect latest code (again).

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/84
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Thomas E Lackey <telackey@bozemanpass.com>
Co-committed-by: Thomas E Lackey <telackey@bozemanpass.com>
2023-04-11 10:58:12 +08:00
fuxiaohei
b79c3aa1a3 feat: add artifact api from gitea server (#103)
add action api for artifacts upload and download.
It's related to https://github.com/go-gitea/gitea/pull/22738

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/103
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: fuxiaohei <fuxiaohei@vip.qq.com>
Co-committed-by: fuxiaohei <fuxiaohei@vip.qq.com>
2023-04-10 21:35:07 +08:00
Jason Song
9c6499ec08 Fix panic when response is nil (#105)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/105
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2023-04-06 21:51:46 +08:00
Jason Song
d139faa40c Supports configuring fetch_timeout and fetch_interval. (#100)
Fix #99.

Fix #94.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/100
Reviewed-by: Zettat123 <zettat123@noreply.gitea.io>
2023-04-06 10:57:36 +08:00
Jason Song
220efa69c0 Refactor to new framework (#98)
- Adjust directory structure
```text
├── internal
│   ├── app
│   │   ├── artifactcache
│   │   ├── cmd
│   │   ├── poll
│   │   └── run
│   └── pkg
│       ├── client
│       ├── config
│       ├── envcheck
│       ├── labels
│       ├── report
│       └── ver
└── main.go
```
- New pkg `labels` to parse label
- New pkg `report` to report logs to Gitea
- Remove pkg `engine`, use `envcheck` to check if docker running.
- Rewrite `runtime` to `run`
- Rewrite `poller` to `poll`
- Simplify some code and remove what's useless.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/98
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2023-04-04 21:32:04 +08:00
Jason Song
df3cb60978 Config for container network (#96)
Fix #66

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/96
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-04-04 14:32:01 +08:00
Jason Song
7e7096e60b Refactor environment variables to configuration and registration (#90)
Close #21.

Refactor environment variables to configuration file (config.yaml) and registration file (.runner).

The old environment variables are still supported, but warning logs will be printed.

Like:

```text
$ GITEA_DEBUG=true ./act_runner -c config.yaml daemon
INFO[0000] Starting runner daemon
WARN[0000] env GITEA_DEBUG has been ignored because config file is used

$ GITEA_DEBUG=true ./act_runner daemon
INFO[0000] Starting runner daemon
WARN[0000] env GITEA_DEBUG will be deprecated, please use config file instead
```

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/90
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-04-02 22:41:48 +08:00
telackey
8eea12dd78 Add CLI flag for specifying the Docker image to use. (#83)
Since the `exec` command does not use labels from `.runner`, there is no existing way to specify which Docker image to use for task execution.

This adds an `--image` flag for specifying it manually.  The default remains `node:16-bullseye`.

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/83
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: telackey <telackey@noreply.gitea.io>
Co-committed-by: telackey <telackey@noreply.gitea.io>
2023-03-29 09:42:53 +08:00
Lunny Xiao
c8fad20f49 handle possible panic (#88)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/88
Reviewed-by: Jason Song <i@wolfogre.com>
2023-03-28 23:51:38 +08:00
Zettat123
1596e4b1fd Fix potential log panic (#82)
If a job uses a [reusable workflow](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#example-of-jobsjob_iduses), the job's steps sequence will be empty.

But in log reporter, we don't check the length of `r.state.Steps`, which may cause panic.

``` go
if v, ok := entry.Data["stepNumber"]; ok {
	if v, ok := v.(int); ok {
		step = r.state.Steps[v]
	}
}
```

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/82
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-03-28 11:49:09 +08:00
Jason Song
c9e076db68 Get outbound IP in multiple ways or disable cache server if failed to init (#74)
Fix #64 (incompletely).

It's still not ideal. It makes more sense to use the gateway IP address of container network as outbound IP of cache server. However, this requires act to cooperate, some think like:

- act creates the network for new container, and returns the network to runner.
- runner extracts the gateway IP in the network.
- runner uses the gateway IP as outbound IP, and pass it to act as cache server endpoint.
- act It continues to create the container with the created network.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/74
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-24 17:55:13 +08:00
Jason Song
bc1842d649 Vet code (#73)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/73
2023-03-24 15:10:39 +08:00
Jason Song
90b8cc6a7a Clarify labels (#69)
The label will follow the format `label[:schema[:args]]`, and the schema will be `host` if it's omitted. So

- `ubuntu:docker://node:18`: Run jobs with label `ubuntu` via docker with image `node:18`
- `ubuntu:host`: Run jobs with label `ubuntu` on the host directly.
- `ubuntu`: Same as `ubuntu:host`.
- `ubuntu:vm:ubuntu-latest`: (Just a example, not Implemented) Run jobs with label `ubuntu` via virtual machine with iso `ubuntu-latest`.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/69
Reviewed-by: Zettat123 <zettat123@noreply.gitea.io>
Reviewed-by: wxiaoguang <wxiaoguang@noreply.gitea.io>
2023-03-23 20:48:33 +08:00
Jason Song
4d5a35ac65 Upgrade act (#68)
Related to https://gitea.com/gitea/act/pulls/27

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/68
2023-03-23 13:33:17 +08:00
Zettat123
8f81f40d62 Fix failed to create container if the runner works in root dir (#67)
Fix #56

This PR uses the `preset.Repository` as a part of the workdir and use `filepath.FromSlash` to convert the slash characters.

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/67
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
2023-03-23 09:41:22 +08:00
Benjamin Loison
9f90cba993 Correct spaces in README.md and in Enter the runner name when running ./act_runner register (#65)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/65
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Benjamin Loison <benjamin.loison@orange.fr>
Co-committed-by: Benjamin Loison <benjamin.loison@orange.fr>
2023-03-22 14:48:35 +08:00
Jason Song
48b05a0ca8 Upgrade act to support go actions (#62)
See:
- https://gitea.com/gitea/act/pulls/20
- https://gitea.com/gitea/act/pulls/22
- https://gitea.com/gitea/act/pulls/26

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/62
Reviewed-by: Zettat123 <zettat123@noreply.gitea.io>
2023-03-21 16:33:08 +08:00
techknowlogick
9eb8b08a69 checksum and compress 2023-03-18 01:58:21 -04:00
Jason Song
4d868b7f3c Update act to v0.243 (#54)
- Update act to v0.243.1
- Disable artifacts server when run daemon.
- Adjust cmd.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/54
Reviewed-by: Zettat123 <zettat123@noreply.gitea.io>
2023-03-17 09:45:46 +08:00
Lunny Xiao
63a57edaa3 check go version when build (#53)
Fix #51

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/53
2023-03-16 11:37:08 +08:00
Lunny Xiao
5180cd56e1 Support cache on ci (#47)
Fix #46

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/47
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-15 12:28:18 +08:00
sillyguodong
370989b2d0 Print the kind of event that trigger the actions (#48)
![image](/attachments/28a866c6-3134-477d-a8c8-d624fa90db0b)

Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/48
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
2023-03-15 09:44:13 +08:00
Lunny Xiao
71f470d670 Fix make don't rebuild when go.mod changed (#49)
Fix #13

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/49
Reviewed-by: delvh <dev.lh@web.de>
2023-03-14 18:43:05 +08:00
Lunny Xiao
c0c363bf59 Update readme to add pre-built binary download links (#45)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/45
Reviewed-by: techknowlogick <techknowlogick@noreply.gitea.io>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-03-14 13:39:12 +08:00
sillyguodong
0d71463662 Inject version when building and report version to Gitea via log and header (#43)
close #42
1. Inject runner version when `make build`
After building, executing command line: `./act_runner -v` or `./act_runner --version`, the version of runner is printed.
![image](/attachments/e25efbd3-79b3-49a5-b93f-42646d42c707)

2. In `Actions` UI:
![image](/attachments/36c57470-2a1d-4796-9eb0-de3988ab88e1)

3. Set request header in http client interceptor.

Co-authored-by: sillyguodong <gedong_1994@163.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/43
Reviewed-by: delvh <dev.lh@web.de>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <sillyguodong@noreply.gitea.io>
Co-committed-by: sillyguodong <sillyguodong@noreply.gitea.io>
2023-03-13 18:57:35 +08:00
HesterG
ebcf341de7 Fix wrong last step duration when job failed (#41)
This PR is to fix the wrong last step duration when job failed like shown in the screenshot.
The reason is because when job failed, `Fire` function did not pass in Time, and `r.state.StoppedAt` is by default set to `0001-01-01 08:05:43 +0805 LMT`, which is later on reported to gitea by `UpdateTask`, which calls `UpdateTaskByState` to update the `task.Stopped`, and `task.Stopped` is used in `FullSteps`, resulting in wrong calcaulation of last step duration.

Co-authored-by: nickname <test@123.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/41
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: HesterG <hesterg@noreply.gitea.io>
Co-committed-by: HesterG <hesterg@noreply.gitea.io>
2023-03-08 21:32:54 +08:00
a1012112796
14334f76ed Add exec subcommand for runner so that we can run the tasks locally(#39)
Most codes are copied from https://gitea.com/gitea/act/src/branch/main/cmd
and do some small changes to make it run again

examples:

```SHELL
./act_runner exec -l
./act_runner exec -j lint
./act_runner exec -j lint -n
```

some example result:

![屏幕截图 2023-03-06 135735](/attachments/547bd05c-ade2-41f7-ba60-c9937fa32d5f)

![屏幕截图 2023-03-06 140643](/attachments/e8f48dba-c7f3-4daa-a163-aa9b36b1dc32)

Signed-off-by: a1012112796 <1012112796@qq.com>

fix #32

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/39
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: a1012112796 <1012112796@qq.com>
Co-committed-by: a1012112796 <1012112796@qq.com>
2023-03-08 10:55:31 +08:00
Zettat123
f24e0721dc Add runner name to log (#37)
User can get the name of the runner that executed the specified job.
![image](/attachments/61328f68-7223-4345-85c7-ac08781e81db)

Co-authored-by: Zettat123 <zettat123@gmail.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/37
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@noreply.gitea.io>
Co-committed-by: Zettat123 <zettat123@noreply.gitea.io>
2023-03-06 18:42:07 +08:00
ChristopherHX
e36300ce28 fix docker executor on windows and local actions (#34)
If the Workdir field doesn't ends with the filepath seperator,
bad things happen

Fixes #33

Sample for host mode on windows, needs be adjusted for linux e.g. replace pwsh with bash
Also fixes
```yaml
on: push
jobs:
  _:
    runs-on: self-hosted
    steps:
    - uses: actions/checkout@v3
      with:
        path: subdir/action
    - uses: ./subdir/action
```

with an action.yml in the same repo
```yaml
runs:
  using: composite
  steps:
    - run: |
        echo "Hello World"
      shell: pwsh
```

Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/34
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: ChristopherHX <christopherhx@noreply.gitea.io>
Co-committed-by: ChristopherHX <christopherhx@noreply.gitea.io>
2023-03-06 13:24:32 +08:00
techknowlogick
09ddbe166f disable more arch 2023-03-01 13:00:18 +08:00
techknowlogick
da0713e629 disable arch that modernc does not support 2023-03-01 12:50:50 +08:00
techknowlogick
bbd055ac3b run nightly on ubuntu runner 2023-03-01 12:32:00 +08:00
techknowlogick
462b2660de disable arches that modernc/sqlite don't complie for 2023-03-01 12:31:27 +08:00
techknowlogick
ebdbfeb54a fix lint error (#30)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/30
Reviewed-by: John Olheiser <john+gitea@jolheiser.com>
2023-03-01 06:40:20 +08:00
Jason Song
436b441cad Support cache (#25)
See [Caching dependencies to speed up workflows](https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows).

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/25
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Reviewed-by: Zettat123 <zettat123@noreply.gitea.io>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-committed-by: Jason Song <i@wolfogre.com>
2023-02-28 23:39:30 +08:00
sillyguodong
552dbcdda9 Add copyright header and gitea-vet (#29)
Add copyright header

Co-authored-by: sillyguodong <gedong_1994@163.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/29
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Zettat123 <zettat123@noreply.gitea.io>
Co-authored-by: sillyguodong <sillyguodong@noreply.gitea.io>
Co-committed-by: sillyguodong <sillyguodong@noreply.gitea.io>
2023-02-28 18:44:46 +08:00
techknowlogick
a50b094c1a update env var 2023-02-27 15:30:35 +08:00
techknowlogick
6cc53f16d8 use aws runner 2023-02-27 14:42:57 +08:00
techknowlogick
8fcd56dc7b skip dist/config.yaml 2023-02-26 15:40:22 +08:00
techknowlogick
c9318f08e2 skip nightly publish 2023-02-26 15:39:22 +08:00
techknowlogick
c7f8919470 mkdir 2023-02-26 13:35:30 +08:00
techknowlogick
14dfa5cc15 s3 credentials 2023-02-26 13:34:26 +08:00
techknowlogick
99a53a1f4c release to s3 2023-02-26 13:00:36 +08:00
techknowlogick
df2219eeb8 use node16 version of aws cred config 2023-02-26 12:42:50 +08:00
techknowlogick
216f3d1740 connect to aws s3 2023-02-26 12:39:41 +08:00
techknowlogick
8aa186897f disable cache on golang setup 2023-02-26 12:24:49 +08:00
techknowlogick
3fa7707bc1 goreleaser needs go to build binary 2023-02-26 12:21:50 +08:00
techknowlogick
9038442191 pull tags for goreleaser 2023-02-26 12:20:47 +08:00
techknowlogick
c133be12d8 release nightly 2023-02-26 12:18:31 +08:00
techknowlogick
1072e3c383 Goreleaser build nightlies (#27)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/27
2023-02-26 12:16:38 +08:00
Lunny Xiao
a4513405b5 Enable action as CI to test/build/release (#26)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/26
2023-02-24 23:30:09 +08:00
Jason Song
410765b516 Upgrade act (#24)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/24
2023-02-24 14:23:22 +08:00
Jason Song
06b9c3962e Upgrade act (#23)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/23
2023-02-24 12:16:18 +08:00
Jason Song
0b64655a40 Upgrade act (#22)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/22
2023-02-24 10:23:18 +08:00
Jason Song
75820a6a3e Upgrade act to v0.234.2 (#20)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/20
2023-02-15 16:53:23 +08:00
Lunny Xiao
f1869f70b9 Allow request an insecure gitea server (#18)
When deploy a Gitea server with a self-signed HTTPS certification. Runner will be failed when connect to Gitea server. This PR will fix that to allow ignore the HTTPS certification verification.

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/18
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2023-02-15 16:51:14 +08:00
ChristopherHX
990cf93c71 feat: don't require docker (#16)
The only reason docker is really required by now, is that act_runner ping docker.
This change only pings docker if a label with `docker://` is added to the runner.

Plain labels without `:` like `self-hosted` are run directly on the system. Previously the pseudo non docker label `-self-hosted` have been required like this `self-hosted:docker://-self-hosted`, but due to docker ping this still required a dockerd to be pingable.

Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/16
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: ChristopherHX <christopherhx@noreply.gitea.io>
Co-committed-by: ChristopherHX <christopherhx@noreply.gitea.io>
2023-01-27 20:42:02 +08:00
ChristopherHX
f7a52789d9 fix: report job failure on error return (#14)
act_runner only returns the error to the runner, but doesn't update the job result to failure.

Co-authored-by: Christopher Homberger <christopher.homberger@web.de>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/14
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: ChristopherHX <christopherhx@noreply.gitea.io>
Co-committed-by: ChristopherHX <christopherhx@noreply.gitea.io>
2023-01-27 20:18:12 +08:00
Jason Song
cf27d3f300 Update act to 0.234.1 (#12)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/12
2023-01-06 18:32:01 +08:00
Xinyu Zhou
7abe039c31 docs: Add Prerequisites (#11)
Close #10

Docker daemon is required

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/11
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Xinyu Zhou <i@sourcehut.net>
Co-committed-by: Xinyu Zhou <i@sourcehut.net>
2023-01-03 11:13:55 +08:00
Xinyu Zhou
9ba9604256 fix(typo): Use uppercase N for default input prompt (#9)
Runner is already registered, overwrite local config? [y/N]

Reviewed-on: https://gitea.com/gitea/act_runner/pulls/9
Reviewed-by: Jason Song <wolfogre@noreply.gitea.io>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Xinyu Zhou <i@sourcehut.net>
Co-committed-by: Xinyu Zhou <i@sourcehut.net>
2022-12-19 09:06:24 +08:00
Jason Song
6d2200b3d6 Rename to actions (#7)
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/7
2022-12-06 16:37:38 +08:00
Jason Song
0cbdbd36b6 Trim newline in log message (#6)
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/6
2022-12-02 16:07:23 +08:00
Jason Song
0f97dd873a Use default labels with no interactive register (#5)
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/5
2022-12-02 12:01:50 +08:00
Jason Song
8996b9b0e4 Disable HTTP/2 (#4)
We use [connect-go](https://github.com/bufbuild/connect-go) instead of [grpc-go](https://github.com/grpc/grpc-go) because connect-go support HTTP/1.1, that means we can mount the gRPC api on the Gitea server without change the protocol.

So it doesn't make sense that make the runner support both HTTP/1.1 and HTTP/2, and [upgrade the protocol used on Gitea](
ae018b6b48/modules/graceful/server_http.go (L23)) to support HTTP/2 and h2c. Although it works right now, I believe there'll be lots of problems when the Gitea server is behind a reverse proxy.

So let's KISS, we don't touch the http protocol of Gitea, and disable HTTP/2 for runner. And we would support HTTP/2 in the future if we really need it.

Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/4
2022-11-29 10:35:59 +08:00
Lunny Xiao
715d0e85ce Merge pull request 'Fix duplicated log when register failed' (#3) from lunny/act_runner:lunny/fix_register_failure_hint into main
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/3
2022-11-25 00:55:16 +08:00
Lunny Xiao
1ab84ac8e8 Fix duplicated log when register failed 2022-11-25 00:54:32 +08:00
Lunny Xiao
1d50f0f5fd Merge pull request 'Update README about register' (#2) from lunny/act_runner:lunny/update_readme into main
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/2
2022-11-25 00:42:46 +08:00
Lunny Xiao
d1017d752f Update README about register 2022-11-25 00:40:00 +08:00
appleboy
474683c0e8 docs(readme): update some header. (#1)
Reviewed-on: https://gitea.com/gitea/act_runner/pulls/1
2022-11-24 23:19:25 +08:00
Jason Song
03c4c4e116 docs: add quickstart to readme 2022-11-24 16:38:42 +08:00
Jason Song
b3e7d6a62d fix: update container config 2022-11-24 15:38:22 +08:00
Jason Song
2354f5bb18 feat: use specified labels 2022-11-24 15:38:21 +08:00
Jason Song
5781e233c1 fix: limit workers 2022-11-24 15:38:20 +08:00
Jason Song
f05e08a767 chore: update grpc router path 2022-11-24 15:38:19 +08:00
fuxiaohei
86f66eaa45 feat(register): add labels to local runner file 2022-11-24 15:38:18 +08:00
Jason Song
64c9856ba7 fix: set more image 2022-11-24 15:38:17 +08:00
Jason Song
66ec50e427 chore: upgrade act 2022-11-24 15:38:16 +08:00
Jason Song
44988db9f8 chore: replace with code.gitea.io/bots-proto-go 2022-11-24 15:38:15 +08:00
Lunny Xiao
519726b46b Update act 2022-11-24 15:38:14 +08:00
fuxiaohei
3f676a742b feat(register): add label format validation 2022-11-24 15:38:13 +08:00
Bo-Yi.Wu
e9e42e850b chore(runner): remove update runner status
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:38:12 +08:00
Jason Song
8f950ccec0 feat: auto remove containers 2022-11-24 15:38:11 +08:00
fuxiaohei
45de6199d1 feat: parse register arguments and do register when non-interactive mode 2022-11-24 15:38:10 +08:00
Jason Song
ab4e06f977 feat: support post step log 2022-11-24 15:38:09 +08:00
Bo-Yi Wu
cab56996b7 fix(poller): initial ready channel 2022-11-24 15:38:08 +08:00
Bo-Yi Wu
a13ed0c52e feat(poller): support scheduler to fetch task and dispatch to worker 2022-11-24 15:38:07 +08:00
Lunny Xiao
5fc35faf17 Support bot site 2022-11-24 15:38:06 +08:00
fuxiaohei
bfcc6f56f3 feat(cli): make register interactive stages working 2022-11-24 15:38:05 +08:00
fuxiaohei
8f9d7506dc feat(cli): set interactive stages in register command 2022-11-24 15:38:03 +08:00
fuxiaohei
561bfad7c5 feat(cli): add register command basic functions 2022-11-24 15:38:02 +08:00
Jason Song
88ae188699 fix: ignore stuck because of wrong token 2022-11-24 15:38:02 +08:00
Bo-Yi Wu
9830f34d36 fix(poller): graceful shutdown 2022-11-24 15:38:01 +08:00
Lunny Xiao
8cffac65d9 Use normal images which includes git command 2022-11-24 15:38:00 +08:00
Jason Song
2e1326b725 fix: set NoSkipCheckout 2022-11-24 15:37:58 +08:00
Bo-Yi Wu
abdb547b1b chore(poller): add metric to track the worker number
Add metric to track multiple task.
2022-11-24 15:37:53 +08:00
Bo-Yi Wu
d1114da299 chore(runner): wait workflow done before shutdown the service 2022-11-24 15:37:52 +08:00
Jason Song
cbd21b8ec5 feat: avoid reuse of containers 2022-11-24 15:37:51 +08:00
Jason Song
d650bae0b6 feat: replace ciphertext in log 2022-11-24 15:37:50 +08:00
Lunny Xiao
222b5100b6 add new envs 2022-11-24 15:37:48 +08:00
Jason Song
93e42d1465 fix: call RunDaemon 2022-11-24 15:37:47 +08:00
Lunny Xiao
93c8de3ec4 get token 2022-11-24 15:37:46 +08:00
Bo-Yi.Wu
2442cdd8ad fix(runner): check task state field exist.
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:45 +08:00
Bo-Yi.Wu
08c94bb564 chore(runner): cancel task if get the cancel from server
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:44 +08:00
Bo-Yi.Wu
d178051832 chore(runner): add new token in header
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:43 +08:00
Jason Song
2f879c41c4 chore: remove tracing codes 2022-11-24 15:37:42 +08:00
Jason Song
41b5fa6b55 fix: update step result 2022-11-24 15:37:41 +08:00
Lunny Xiao
378966e45f add forgeinstance 2022-11-24 15:37:40 +08:00
Lunny Xiao
9c7d2be7c6 Fix bug 2022-11-24 15:37:39 +08:00
Bo-Yi.Wu
e08495af09 chore(runner): remove client secret and add UUID in runner
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:38 +08:00
Bo-Yi.Wu
bf5e3dc302 chore(runner): add runner uuid in http header.
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:37 +08:00
Bo-Yi.Wu
a503f7429f chore(runner): add new register command
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:36 +08:00
Jason Song
cce60d466b fix: need more time to run task 2022-11-24 15:37:35 +08:00
Bo-Yi Wu
7bebd2bbad chore(runner): update runner status when start job 2022-11-24 15:37:34 +08:00
Bo-Yi Wu
d4c1515f4e chore(runner): update runner status if start program 2022-11-24 15:37:32 +08:00
fuxiaohei
45b0429b21 chore(poller): add error retry limit 2022-11-24 15:37:30 +08:00
Bo-Yi Wu
dada0730b0 chore(runner): add default label ""self-hosted"
see https://docs.github.com/en/actions/hosting-your-own-runners/using-self-hosted-runners-in-a-workflow#using-default-labels-to-route-jobs
2022-11-24 15:37:27 +08:00
Bo-Yi.Wu
c161a48a0a chore(register): update proto file.
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:26 +08:00
Bo-Yi.Wu
3a1503138b chore(runner): refactor register flow
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:24 +08:00
Bo-Yi.Wu
7486d2ab91 chore(runner): update fetch job request
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:22 +08:00
Bo-Yi.Wu
82431d8e11 chore(runner): support update log and task
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:21 +08:00
Bo-Yi.Wu
20c3d85ba9 refactor(task): execute single task with gRPC data 2022-11-24 15:37:19 +08:00
Bo-Yi.Wu
5051e4aebd chore(runtime): check error message
data lock by another runner.

Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:18 +08:00
Bo-Yi.Wu
d3d56ed0ef chore(runtime): fetch build data
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:17 +08:00
Bo-Yi.Wu
631f801aa6 chore(gRPC): fetch new stage data.
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:16 +08:00
fuxiaohei
6030610c04 feat: task can report step and final result 2022-11-24 15:37:14 +08:00
Bo-Yi.Wu
9be39b8cd4 chore(gRPC): add update and update step for client
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:13 +08:00
Bo-Yi.Wu
f2fb8798fa chore(gRPC): add request interface client
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:11 +08:00
Bo-Yi.Wu
449388f3ab chore(gRPC): register new runner
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:10 +08:00
Bo-Yi.Wu
a3e9bbed25 chore: Add makefile
Signed-off-by: Bo-Yi.Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:09 +08:00
Gitea
3281c6637d feat: fill default task runner 2022-11-24 15:37:08 +08:00
Gitea
c5edbbd004 feat: move main task logic to runtime package 2022-11-24 15:37:06 +08:00
GiteaBot
aa765ea4d0 chore(go): rename module name
Signed-off-by: GiteaBot <teabot@gitea.io>
2022-11-24 15:37:03 +08:00
GiteaBot
307c2c63de chore(proto): replace import path.
Signed-off-by: GiteaBot <teabot@gitea.io>
2022-11-24 15:37:02 +08:00
Bo-Yi Wu
7d55fd57c9 chore(piepline): add runtime package.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2022-11-24 15:37:01 +08:00
Bo-Yi Wu
bca586ffd0 chore: Add time sleep 1 second.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2022-11-24 15:36:58 +08:00
Bo-Yi Wu
d359276fe1 chore(client): support gRPC and gRPC web protocol
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2022-11-24 15:36:57 +08:00
Bo-Yi Wu
0b885c5e5f chore(poller): Add poller package
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2022-11-24 15:36:56 +08:00
Bo-Yi Wu
4d7ef95d40 chore(proto): Add ping request.
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2022-11-24 15:36:54 +08:00
Bo-Yi Wu
14a345504f refactor: docker engine with options 2022-11-24 15:36:51 +08:00
Bo-Yi Wu
4eb5213294 chore(engine): ping docker daemon
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2022-11-24 15:36:49 +08:00
Bo-Yi Wu
9225a3a856 chore(config): remove zerolog
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2022-11-24 15:36:48 +08:00
Lunny Xiao
b83f36674b Fix build 2022-11-24 15:36:47 +08:00
fuxiaohei
dfcb104af0 feat: add task log output hook 2022-11-24 15:36:46 +08:00
Lunny Xiao
3b7ac17410 Use fork instead of act 2022-11-24 15:36:45 +08:00
Lunny Xiao
906d52ae0b Use gitea.com/gitea/act replace original 2022-11-24 15:36:31 +08:00
Bo-Yi Wu
aa9ed05903 chore: load config from env file 2022-11-24 15:36:30 +08:00
Bo-Yi Wu
5213612033 chore: remove unused logrous package 2022-11-24 15:36:29 +08:00
Bo-Yi Wu
518aa1b2a7 chore: add .golangci config 2022-11-24 15:36:28 +08:00
Bo-Yi Wu
827bd953e7 chore: fix lint error 2022-11-24 15:36:26 +08:00
Bo-Yi Wu
e54de37242 refactor: Add graceful shutdown signal notify func 2022-11-24 15:36:25 +08:00
Bo-Yi Wu
08282a519f stash
Signed-off-by: Bo-Yi Wu <appleboy.tw@gmail.com>
2022-11-24 15:36:23 +08:00
Lunny Xiao
db7ee2eaf4 log 2022-11-24 15:36:22 +08:00
Lunny Xiao
c7cb750616 refactor functions 2022-11-24 15:36:21 +08:00
Lunny Xiao
5903c08c14 update protocol 2022-11-24 15:36:20 +08:00
Lunny Xiao
2babadbc94 Add websocket 2022-11-24 15:36:19 +08:00
Lunny Xiao
e397fcc259 Add license 2022-11-24 15:36:16 +08:00
Lunny Xiao
eee792a019 init project 2022-04-27 17:45:53 +08:00
1975 changed files with 26065 additions and 490685 deletions

0
.actrc
View File

52
.dockerignore Normal file
View File

@@ -0,0 +1,52 @@
# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
*.so
# Folders
_obj
_test
# IntelliJ
.idea
# Goland's output filename can not be set manually
/go_build_*
# MS VSCode
.vscode
__debug_bin*
# Architecture specific extensions/prefixes
*.[568vq]
[568vq].out
*.cgo1.go
*.cgo2.c
_cgo_defun.c
_cgo_gotypes.go
_cgo_export.*
_testmain.go
*.exe
*.test
*.prof
*coverage.out
coverage.all
coverage.txt
cpu.out
*.db
*.log
/gitea-runner
/debug
/bin
/dist
/.env
/.runner
/config.yaml
/Dockerfile
.DS_Store

View File

@@ -1,21 +1,19 @@
root = true
[*]
indent_style = space
indent_size = 2
tab_width = 2
end_of_line = lf
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
insert_final_newline = true
[*.{go}]
indent_style = tab
indent_size = 4
[{*.md,*_test.go}]
indent_style = unset
indent_size = unset
[go.*]
indent_style = tab
[**/Dockerfile]
indent_style = space
indent_size = 2
[*.{yml,yaml,js,json,sh,nuspec}]
indent_style = space
indent_size = 2
[Makefile]
indent_style = tab

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* text=auto eol=lf

View File

@@ -0,0 +1,27 @@
name: pr-title
on:
pull_request:
types:
- opened
- edited
- reopened
- synchronize
- ready_for_review
permissions:
contents: read
jobs:
lint-pr-title:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v6
- uses: actions/setup-node@v6
with:
node-version: 24
- run: make lint-pr-title
env:
PR_TITLE: ${{ github.event.pull_request.title }}

View File

@@ -0,0 +1,92 @@
---
name: release-nightly
on:
workflow_dispatch:
push:
branches:
- 'main'
tags:
- '*'
env:
DOCKER_ORG: gitea
DOCKER_LATEST: nightly
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/setup-go@v6
with:
go-version-file: "go.mod"
- name: goreleaser
uses: goreleaser/goreleaser-action@v7
with:
distribution: goreleaser-pro
args: release --nightly
env:
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
AWS_REGION: ${{ secrets.AWS_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
S3_REGION: ${{ secrets.AWS_REGION }}
S3_BUCKET: ${{ secrets.AWS_BUCKET }}
GORELEASER_FORCE_TOKEN: "gitea"
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-image:
runs-on: ubuntu-latest
strategy:
matrix:
variant:
- target: basic
tag_suffix: ""
- target: dind
tag_suffix: "-dind"
- target: dind-rootless
tag_suffix: "-dind-rootless"
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0 # all history for all branches and tags
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v4
- name: Login to DockerHub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Echo the tag
run: echo "${{ env.DOCKER_ORG }}/runner:nightly${{ matrix.variant.tag_suffix }}"
- name: Get Meta
id: meta
run: |
echo REPO_VERSION=$(git describe --tags --always | sed 's/-/+/' | sed 's/^v//') >> $GITHUB_OUTPUT
- name: Build and push
uses: docker/build-push-action@v7
with:
context: .
file: ./Dockerfile
target: ${{ matrix.variant.target }}
platforms: |
linux/amd64
linux/arm64
push: true
tags: |
${{ env.DOCKER_ORG }}/runner:nightly${{ matrix.variant.tag_suffix }}
build-args: |
VERSION=${{ steps.meta.outputs.REPO_VERSION }}

View File

@@ -0,0 +1,100 @@
name: release-tag
on:
push:
tags:
- "*"
jobs:
goreleaser:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0 # all history for all branches and tags
- uses: actions/setup-go@v6
with:
go-version-file: "go.mod"
- name: Import GPG key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@v7
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.PASSPHRASE }}
fingerprint: CC64B1DB67ABBEECAB24B6455FC346329753F4B0
- name: goreleaser
uses: goreleaser/goreleaser-action@v7
with:
distribution: goreleaser-pro
args: release
env:
GORELEASER_KEY: ${{ secrets.GORELEASER_KEY }}
AWS_REGION: ${{ secrets.AWS_REGION }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
S3_REGION: ${{ secrets.AWS_REGION }}
S3_BUCKET: ${{ secrets.AWS_BUCKET }}
GORELEASER_FORCE_TOKEN: "gitea"
GITEA_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
release-image:
runs-on: ubuntu-latest
strategy:
matrix:
variant:
- target: basic
tag_suffix: ""
- target: dind
tag_suffix: "-dind"
- target: dind-rootless
tag_suffix: "-dind-rootless"
container:
image: catthehacker/ubuntu:act-latest
env:
DOCKER_ORG: gitea
DOCKER_LATEST: latest
steps:
- name: Checkout
uses: actions/checkout@v6
with:
fetch-depth: 0 # all history for all branches and tags
- name: Set up QEMU
uses: docker/setup-qemu-action@v4
- name: Set up Docker BuildX
uses: docker/setup-buildx-action@v4
- name: Login to DockerHub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: "Docker meta"
id: docker_meta
uses: docker/metadata-action@v6
with:
images: |
${{ env.DOCKER_ORG }}/runner
tags: |
type=semver,pattern={{major}}.{{minor}}.{{patch}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
flavor: |
latest=true
suffix=${{ matrix.variant.tag_suffix }},onlatest=true
- name: Build and push
uses: docker/build-push-action@v7
with:
context: .
file: ./Dockerfile
target: ${{ matrix.variant.target }}
platforms: |
linux/amd64
linux/arm64
push: true
tags: ${{ steps.docker_meta.outputs.tags }}
build-args: |
VERSION=${{ steps.docker_meta.outputs.version }}

View File

@@ -1,44 +1,44 @@
name: checks
on:
- push
- pull_request
env:
GOPROXY: https://goproxy.io,direct
GOPATH: /go_path
GOCACHE: /go_cache
push:
branches:
- main
pull_request:
jobs:
lint:
name: check and test
runs-on: ubuntu-latest
env:
# The runner image ships a stale docker.io login; point docker at an empty config so
# image pulls go straight to anonymous instead of attempting (and failing) that auth
# first. The path must be a literal: the `runner` context is unavailable in job-level
# env, so `${{ runner.temp }}` would resolve to empty and config.Dir() would fall back
# to ~/.docker with the stale credentials.
DOCKER_CONFIG: /tmp/docker-noauth
steps:
- name: cache go path
id: cache-go-path
uses: https://github.com/actions/cache@v3
- uses: actions/checkout@v6
- uses: actions/setup-go@v6
with:
path: /go_path
key: go_path-${{ github.repository }}-${{ github.ref_name }}
restore-keys: |
go_path-${{ github.repository }}-
go_path-
- name: cache go cache
id: cache-go-cache
uses: https://github.com/actions/cache@v3
with:
path: /go_cache
key: go_cache-${{ github.repository }}-${{ github.ref_name }}
restore-keys: |
go_cache-${{ github.repository }}-
go_cache-
- uses: actions/setup-go@v3
with:
go-version: 1.20
- uses: actions/checkout@v3
- name: vet checks
run: go vet -v ./...
go-version-file: 'go.mod'
- name: prepare anonymous docker config
run: mkdir -p "$DOCKER_CONFIG" && echo '{}' > "$DOCKER_CONFIG/config.json"
# Pre-pull act/runner's two largest base images so a slow pull can't dominate `make test`;
# the rest (alpine/ubuntu) pull on demand, absorbed by the make-test -timeout. The host
# daemon retains them between runs, so this is usually a fast manifest re-check.
- name: pre-pull test images
run: |
for img in node:24-bookworm-slim nginx:alpine; do
for try in 1 2 3; do docker pull "$img" && break || sleep 5; done
done
- name: lint
run: make lint
- name: build
run: go build -v ./...
run: make build
- name: test
run: go test -v ./pkg/jobparser
# TODO test more packages
run: make test
# Build the dind image and run the daemon-facing tests against the docker version it
# ships, catching daemon-level regressions (e.g. gitea/runner#981) before release. Runs
# after `make test` so the images it needs are already present on the host daemon.
- name: test against dind image
run: make test-dind

1
.github/FUNDING.yml vendored
View File

@@ -1 +0,0 @@
github: cplee

View File

@@ -1,88 +0,0 @@
name: Bug report
description: Use this template for reporting bugs/issues.
labels:
- 'kind/bug'
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: textarea
id: act-debug
attributes:
label: Bug report info
render: plain text
description: |
Output of `act --bug-report`
placeholder: |
act --bug-report
validations:
required: true
- type: textarea
id: act-command
attributes:
label: Command used with act
description: |
Please paste your whole command
placeholder: |
act -P ubuntu-latest=node:12 -v -d ...
render: sh
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: Describe issue
description: |
Also tell us what did you expect to happen?
placeholder: |
Describe issue
validations:
required: true
- type: input
id: repo
attributes:
label: Link to GitHub repository
description: |
Provide link to GitHub repository, you can skip it if the repository is private or you don't have it on GitHub, otherwise please provide it as it might help us troubleshoot problem
placeholder: |
https://github.com/nektos/act
validations:
required: false
- type: textarea
id: workflow
attributes:
label: Workflow content
description: |
Please paste your **whole** workflow here
placeholder: |
name: My workflow
on: ['push', 'schedule']
jobs:
test:
runs-on: ubuntu-latest
env:
KEY: VAL
[...]
render: yml
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: |
Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks. Please verify that the log output doesn't contain any sensitive data.
render: sh
placeholder: |
Use `act -v` for verbose output
validations:
required: true
- type: textarea
id: additional-info
attributes:
label: Additional information
placeholder: |
Additional information that doesn't fit elsewhere
validations:
required: false

View File

@@ -1,14 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: Start a discussion
url: https://github.com/nektos/act/discussions/new
about: You can ask for help here!
- name: Ask on Gitter
url: https://gitter.im/nektos/act
about: You can ask for help here!
- name: Ask on Gitter (via Matrix)
url: https://matrix.to/#/#nektos_act:gitter.im?via=gitter.im&via=matrix.org
about: If you prefer Matrix over Gitter
- name: Want to contribute to act?
url: https://github.com/nektos/act/blob/master/CONTRIBUTING.md
about: Be sure to read contributing guidelines!

View File

@@ -1,28 +0,0 @@
name: Feature request
description: Use this template for requesting a feature/enhancement.
labels:
- 'kind/feature-request'
body:
- type: markdown
attributes:
value: |
Please note that incompatibility with GitHub Actions should be opened as a bug report, not a new feature.
- type: input
id: act-version
attributes:
label: Act version
description: |
What version of `act` are you using? Version can be obtained via `act --version`
If you've built it from source, please provide commit hash
placeholder: |
act --version
validations:
required: true
- type: textarea
id: feature
attributes:
label: Feature description
description: Describe feature that you would like to see
placeholder: ...
validations:
required: true

View File

@@ -1,18 +0,0 @@
name: Wiki issue
description: Report issue/improvement ragarding documentation (wiki)
labels:
- 'kind/discussion'
- 'area/docs'
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this issue!
- type: textarea
id: details
attributes:
label: Details
description: |
Describe issue
validations:
required: true

View File

@@ -1,20 +0,0 @@
FROM alpine:3.17
ARG CHOCOVERSION=1.1.0
RUN apk add --no-cache bash ca-certificates git \
&& apk --no-cache --repository http://dl-cdn.alpinelinux.org/alpine/edge/testing add mono mono-dev \
&& cert-sync /etc/ssl/certs/ca-certificates.crt \
&& wget "https://github.com/chocolatey/choco/archive/${CHOCOVERSION}.tar.gz" -O- | tar -xzf - \
&& cd choco-"${CHOCOVERSION}" \
&& chmod +x build.sh zip.sh \
&& ./build.sh -v \
&& mv ./code_drop/chocolatey/console /opt/chocolatey \
&& mkdir -p /opt/chocolatey/lib \
&& rm -rf /choco-"${CHOCOVERSION}" \
&& apk del mono-dev \
&& rm -rf /var/cache/apk/*
ENV ChocolateyInstall=/opt/chocolatey
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

View File

@@ -1,16 +0,0 @@
name: 'Chocolatey Packager'
description: 'Create the choco package and push it'
inputs:
version:
description: 'Version of package'
required: false
apiKey:
description: 'API Key for chocolately'
required: false
push:
description: 'Option for if package is going to be pushed'
required: false
default: 'false'
runs:
using: 'docker'
image: 'Dockerfile'

View File

@@ -1,31 +0,0 @@
#!/bin/bash
set -e
function choco {
mono /opt/chocolatey/choco.exe "$@" --allow-unofficial --nocolor
}
function get_version {
local version=${INPUT_VERSION:-$(git describe --tags)}
version=(${version//[!0-9.-]/})
local version_parts=(${version//-/ })
version=${version_parts[0]}
if [ ${#version_parts[@]} -gt 1 ]; then
version=${version_parts}.${version_parts[1]}
fi
echo "$version"
}
## Determine the version to pack
VERSION=$(get_version)
echo "Packing version ${VERSION} of act"
rm -f act-cli.*.nupkg
mkdir -p tools
cp LICENSE tools/LICENSE.txt
cp VERIFICATION tools/VERIFICATION.txt
cp dist/act_windows_amd64_v1/act.exe tools/
choco pack act-cli.nuspec --version ${VERSION}
if [[ "$INPUT_PUSH" == "true" ]]; then
choco push act-cli.${VERSION}.nupkg --api-key ${INPUT_APIKEY} -s https://push.chocolatey.org/ --timeout 180
fi

View File

@@ -1,77 +0,0 @@
name: 'run-tests'
description: 'Runs go test and upload a step summary'
inputs:
filter:
description: 'The go test pattern for the tests to run'
required: false
default: ''
upload-logs-name:
description: 'Choose the name of the log artifact'
required: false
default: logs-${{ github.job }}-${{ strategy.job-index }}
upload-logs:
description: 'If true uploads logs of each tests as an artifact'
required: false
default: 'true'
runs:
using: composite
steps:
- uses: actions/github-script@v6
with:
github-token: none # No reason to grant access to the GITHUB_TOKEN
script: |
let myOutput = '';
var fs = require('fs');
var uploadLogs = process.env.UPLOAD_LOGS === 'true';
if(uploadLogs) {
await io.mkdirP('logs');
}
var filename = null;
const options = {};
options.ignoreReturnCode = true;
options.env = Object.assign({}, process.env);
delete options.env.ACTIONS_RUNTIME_URL;
delete options.env.ACTIONS_RUNTIME_TOKEN;
delete options.env.ACTIONS_CACHE_URL;
options.listeners = {
stdout: (data) => {
for(line of data.toString().split('\n')) {
if(/^\s*(===\s[^\s]+\s|---\s[^\s]+:\s)/.test(line)) {
if(uploadLogs) {
var runprefix = "=== RUN ";
if(line.startsWith(runprefix)) {
filename = "logs/" + line.substring(runprefix.length).replace(/[^A-Za-z0-9]/g, '-') + ".txt";
fs.writeFileSync(filename, line + "\n");
} else if(filename) {
fs.appendFileSync(filename, line + "\n");
filename = null;
}
}
myOutput += line + "\n";
} else if(filename) {
fs.appendFileSync(filename, line + "\n");
}
}
}
};
var args = ['test', '-v', '-cover', '-coverprofile=coverage.txt', '-covermode=atomic', '-timeout', '20m'];
var filter = process.env.FILTER;
if(filter) {
args.push('-run');
args.push(filter);
}
args.push('./...');
var exitcode = await exec.exec('go', args, options);
if(process.env.GITHUB_STEP_SUMMARY) {
core.summary.addCodeBlock(myOutput);
await core.summary.write();
}
process.exit(exitcode);
env:
FILTER: ${{ inputs.filter }}
UPLOAD_LOGS: ${{ inputs.upload-logs }}
- uses: actions/upload-artifact@v3
if: always() && inputs.upload-logs == 'true' && !env.ACT
with:
name: ${{ inputs.upload-logs-name }}
path: logs

View File

@@ -1,15 +0,0 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'weekly'
- package-ecosystem: 'gomod'
directory: '/'
schedule:
interval: 'weekly'

View File

@@ -1 +0,0 @@
test-*.yml

View File

@@ -1,181 +0,0 @@
name: checks
on: [pull_request, workflow_dispatch]
concurrency:
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.ref }}
env:
ACT_OWNER: ${{ github.repository_owner }}
ACT_REPOSITORY: ${{ github.repository }}
CGO_ENABLED: 0
jobs:
lint:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-go@v4
with:
go-version-file: go.mod
check-latest: true
- uses: golangci/golangci-lint-action@v3.7.0
with:
version: v1.53
only-new-issues: true
- uses: megalinter/megalinter/flavors/go@v7.3.0
env:
DEFAULT_BRANCH: master
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_ALL_CODEBASE: false
GITHUB_STATUS_REPORTER: ${{ !env.ACT }}
GITHUB_COMMENT_REPORTER: ${{ !env.ACT }}
test-linux:
name: test-linux
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 2
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- uses: actions/setup-go@v4
with:
go-version-file: go.mod
check-latest: true
- uses: actions/cache@v3
if: ${{ !env.ACT }}
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: Run Tests
uses: ./.github/actions/run-tests
with:
upload-logs-name: logs-linux
- name: Run act from cli
run: go run main.go -P ubuntu-latest=node:16-buster-slim -C ./pkg/runner/testdata/ -W ./basic/push.yml
- name: Upload Codecov report
uses: codecov/codecov-action@v3.1.4
with:
files: coverage.txt
fail_ci_if_error: true # optional (default = false)
test-host:
strategy:
matrix:
os:
- windows-latest
- macos-latest
name: test-${{matrix.os}}
runs-on: ${{matrix.os}}
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 2
- uses: actions/setup-go@v4
with:
go-version-file: go.mod
check-latest: true
- name: Run Tests
uses: ./.github/actions/run-tests
with:
filter: '^TestRunEventHostEnvironment$'
upload-logs-name: logs-${{ matrix.os }}
snapshot:
name: snapshot
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version-file: go.mod
check-latest: true
- uses: actions/cache@v3
if: ${{ !env.ACT }}
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --snapshot --clean
- name: Capture x86_64 (64-bit) Linux binary
if: ${{ !env.ACT }}
uses: actions/upload-artifact@v3
with:
name: act-linux-amd64
path: dist/act_linux_amd64_v1/act
- name: Capture i386 (32-bit) Linux binary
if: ${{ !env.ACT }}
uses: actions/upload-artifact@v3
with:
name: act-linux-i386
path: dist/act_linux_386/act
- name: Capture arm64 (64-bit) Linux binary
if: ${{ !env.ACT }}
uses: actions/upload-artifact@v3
with:
name: act-linux-arm64
path: dist/act_linux_arm64/act
- name: Capture armv6 (32-bit) Linux binary
if: ${{ !env.ACT }}
uses: actions/upload-artifact@v3
with:
name: act-linux-armv6
path: dist/act_linux_arm_6/act
- name: Capture armv7 (32-bit) Linux binary
if: ${{ !env.ACT }}
uses: actions/upload-artifact@v3
with:
name: act-linux-armv7
path: dist/act_linux_arm_7/act
- name: Capture x86_64 (64-bit) Windows binary
if: ${{ !env.ACT }}
uses: actions/upload-artifact@v3
with:
name: act-windows-amd64
path: dist/act_windows_amd64_v1/act.exe
- name: Capture i386 (32-bit) Windows binary
if: ${{ !env.ACT }}
uses: actions/upload-artifact@v3
with:
name: act-windows-i386
path: dist/act_windows_386/act.exe
- name: Capture arm64 (64-bit) Windows binary
if: ${{ !env.ACT }}
uses: actions/upload-artifact@v3
with:
name: act-windows-arm64
path: dist/act_windows_arm64/act.exe
- name: Capture armv7 (32-bit) Windows binary
if: ${{ !env.ACT }}
uses: actions/upload-artifact@v3
with:
name: act-windows-armv7
path: dist/act_windows_arm_7/act.exe
- name: Capture x86_64 (64-bit) MacOS binary
if: ${{ !env.ACT }}
uses: actions/upload-artifact@v3
with:
name: act-macos-amd64
path: dist/act_darwin_amd64_v1/act
- name: Capture arm64 (64-bit) MacOS binary
if: ${{ !env.ACT }}
uses: actions/upload-artifact@v3
with:
name: act-macos-arm64
path: dist/act_darwin_arm64/act
- name: Chocolatey
uses: ./.github/actions/choco
with:
version: v0.0.0-pr

View File

@@ -1,29 +0,0 @@
name: promote
on:
schedule:
- cron: '0 2 1 * *'
workflow_dispatch: {}
jobs:
release:
name: promote
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
ref: master
token: ${{ secrets.GORELEASER_GITHUB_TOKEN }}
- uses: fregante/setup-git-user@v2
- uses: actions/setup-go@v4
with:
go-version-file: go.mod
check-latest: true
- uses: actions/cache@v3
if: ${{ !env.ACT }}
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- run: make promote

View File

@@ -1,61 +0,0 @@
name: release
on:
push:
tags:
- v*
jobs:
release:
name: release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: actions/setup-go@v4
with:
go-version-file: go.mod
check-latest: true
- uses: actions/cache@v3
if: ${{ !env.ACT }}
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
restore-keys: |
${{ runner.os }}-go-
- name: GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
version: latest
args: release --clean
env:
GITHUB_TOKEN: ${{ secrets.GORELEASER_GITHUB_TOKEN }}
- name: Winget
uses: vedantmgoyal2009/winget-releaser@v2
with:
identifier: nektos.act
installers-regex: '_Windows_\w+\.zip$'
token: ${{ secrets.WINGET_TOKEN }}
- name: Chocolatey
uses: ./.github/actions/choco
with:
version: ${{ github.ref }}
apiKey: ${{ secrets.CHOCO_APIKEY }}
push: true
- name: GitHub CLI extension
uses: actions/github-script@v6
with:
github-token: ${{ secrets.GORELEASER_GITHUB_TOKEN }}
script: |
const mainRef = (await github.rest.git.getRef({
owner: 'nektos',
repo: 'gh-act',
ref: 'heads/main',
})).data;
console.log(mainRef);
github.rest.git.createRef({
owner: 'nektos',
repo: 'gh-act',
ref: context.ref,
sha: mainRef.object.sha,
});

View File

@@ -1,23 +0,0 @@
name: 'Close stale issues'
on:
schedule:
- cron: '0 0 * * *'
jobs:
stale:
name: Stale
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v8
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Issue is stale and will be closed in 14 days unless there is new activity'
stale-pr-message: 'PR is stale and will be closed in 14 days unless there is new activity'
stale-issue-label: 'stale'
exempt-issue-labels: 'stale-exempt,kind/feature-request'
stale-pr-label: 'stale'
exempt-pr-labels: 'stale-exempt'
remove-stale-when-updated: 'True'
operations-per-run: 500
days-before-stale: 180
days-before-close: 14

44
.gitignore vendored
View File

@@ -1,34 +1,14 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
/dist/
.todo
*.nupkg
.vscode/
.idea/
# Path for actions cache created when running tests
pkg/runner/act/
# act binary
dist/local/act
coverage.txt
/gitea-runner
.env
.secrets
!/act/runner/testdata/secrets/.env
.runner
coverage.txt
/config.yaml
# megalinter
report/
act
# Jetbrains
.idea
# MS VSCode
.vscode
__debug_bin
# gorelease binary folder
/dist

View File

@@ -1 +0,0 @@
b910a42edfab7a02b08a52ecef203fd419725642:pkg/container/testdata/docker-pull-options/config.json:generic-api-key:4

View File

@@ -1,51 +1,125 @@
# Minimum golangci-lint version required: v1.46.0
run:
timeout: 3m
skip-dirs:
- report # megalinter results+fixes
linters-settings:
gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 15
gocritic:
disabled-checks:
- ifElseChain
importas:
aliases:
- pkg: 'github.com/sirupsen/logrus'
alias: log
- pkg: 'github.com/stretchr/testify/assert'
alias: assert
depguard:
rules:
main:
deny:
- pkg: github.com/pkg/errors
desc: Please use "errors" package from standard library
- pkg: gotest.tools/v3
desc: Please keep tests unified using only github.com/stretchr/testify
- pkg: log
desc: Please keep logging unified using only github.com/sirupsen/logrus
version: "2"
output:
sort-order:
- file
linters:
default: none
enable:
- megacheck
- govet
- revive
- gocyclo
- gosec
- unconvert
- dupl
- nakedret
- prealloc
- exportloopref
- gocritic
- goimports
- whitespace
- misspell
- bidichk
- bodyclose
- depguard
- importas
- contextcheck
- dupl
- errcheck
- forbidigo
- gocheckcompilerdirectives
- gocritic
- goheader
- govet
- ineffassign
- mirror
- modernize
- nakedret
- nilnil
- nolintlint
- perfsprint
- revive
- staticcheck
- testifylint
- unconvert
- unparam
- unused
- usestdlibvars
- usetesting
- wastedassign
settings:
depguard:
rules:
main:
deny:
- pkg: io/ioutil
desc: use os or io instead
- pkg: golang.org/x/exp
desc: it's experimental and unreliable
- pkg: github.com/pkg/errors
desc: use builtin errors package instead
nolintlint:
allow-unused: false
require-explanation: true
require-specific: true
gocritic:
enabled-checks:
- equalFold
disabled-checks:
- ifElseChain
revive:
severity: error
rules:
- name: blank-imports
- name: constant-logical-expr
- name: context-as-argument
- name: context-keys-type
- name: dot-imports
- name: empty-lines
- name: error-return
- name: error-strings
- name: exported
- name: identical-branches
- name: if-return
- name: increment-decrement
- name: modifies-value-receiver
- name: package-comments
- name: redefines-builtin-id
- name: superfluous-else
- name: time-naming
- name: unexported-return
- name: var-declaration
- name: var-naming
staticcheck:
checks:
- all
- -ST1005
usetesting:
os-temp-dir: true
perfsprint:
concat-loop: false
govet:
enable:
- nilness
- unusedwrite
goheader:
values:
regexp:
HEADER: 'Copyright \d{4} The Gitea Authors\. All rights reserved\.(\nCopyright [^\n]+)*\nSPDX-License-Identifier: MIT'
template: '{{ HEADER }}'
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
rules:
- linters:
- forbidigo
path: cmd
issues:
max-issues-per-linter: 0
max-same-issues: 0
formatters:
enable:
- gci
- gofumpt
settings:
gci:
custom-order: true
sections:
- standard
- prefix(gitea.com/gitea/runner)
- blank
- default
gofumpt:
extra-rules: true
exclusions:
generated: lax
run:
timeout: 10m

12
.goreleaser.checksum.sh Normal file
View File

@@ -0,0 +1,12 @@
#!/bin/bash
set -e
if [ -z "$1" ]; then
echo "usage: $0 <path>"
exit 1
fi
SUM=$(shasum -a 256 "$1" | cut -d' ' -f1)
BASENAME=$(basename "$1")
echo -n "${SUM} ${BASENAME}" > "$1".sha256

122
.goreleaser.yaml Normal file
View File

@@ -0,0 +1,122 @@
version: 2
project_name: gitea-runner
before:
hooks:
- go mod tidy
builds:
- env:
- CGO_ENABLED=0
goos:
- darwin
- linux
- windows
- freebsd
goarch:
- amd64
- arm
- arm64
- loong64
- s390x
- riscv64
goarm:
- "5"
- "6"
- "7"
ignore:
- goos: darwin
goarch: arm
- goos: darwin
goarch: ppc64le
- goos: darwin
goarch: s390x
- goos: windows
goarch: ppc64le
- goos: windows
goarch: s390x
- goos: windows
goarch: arm
goarm: "5"
- goos: windows
goarch: arm
goarm: "6"
- goos: windows
goarch: arm
goarm: "7"
- goos: windows
goarch: arm64
- goos: freebsd
goarch: ppc64le
- goos: freebsd
goarch: s390x
- goos: freebsd
goarch: arm
goarm: "5"
- goos: freebsd
goarch: arm
goarm: "6"
- goos: freebsd
goarch: arm
goarm: "7"
- goos: freebsd
goarch: arm64
flags:
- -trimpath
ldflags:
- -s -w -X gitea.com/gitea/runner/internal/pkg/ver.version={{ .Summary }}
binary: >-
{{ .ProjectName }}-
{{- .Version }}-
{{- .Os }}-
{{- if eq .Arch "amd64" }}amd64
{{- else if eq .Arch "amd64_v1" }}amd64
{{- else if eq .Arch "386" }}386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}-{{ .Arm }}{{ end }}
no_unique_dist_dir: true
hooks:
post:
- cmd: xz -k -9 {{ .Path }}
dir: ./dist/
- cmd: sh .goreleaser.checksum.sh {{ .Path }}
- cmd: sh .goreleaser.checksum.sh {{ .Path }}.xz
blobs:
-
provider: s3
bucket: "{{ .Env.S3_BUCKET }}"
region: "{{ .Env.S3_REGION }}"
directory: "gitea-runner/{{.Version}}"
extra_files:
- glob: ./**.xz
- glob: ./**.sha256
archives:
- format: binary
name_template: "{{ .Binary }}"
allow_different_binary_count: true
checksum:
name_template: 'checksums.txt'
extra_files:
- glob: ./**.xz
snapshot:
version_template: "{{ .Branch }}-devel"
nightly:
version_template: "nightly"
gitea_urls:
api: https://gitea.com/api/v1
download: https://gitea.com
release:
extra_files:
- glob: ./**.xz
- glob: ./**.xz.sha256
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
# vim: set ts=2 sw=2 tw=0 fo=cnqoj

View File

@@ -1,50 +0,0 @@
before:
hooks:
- go mod tidy
builds:
- env:
- CGO_ENABLED=0
goos:
- darwin
- linux
- windows
goarch:
- amd64
- '386'
- arm64
- arm
goarm:
- '6'
- '7'
ignore:
- goos: windows
goarm: '6'
checksum:
name_template: 'checksums.txt'
archives:
- name_template: >-
{{ .ProjectName }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else if eq .Arch "386" }}i386
{{- else }}{{ .Arch }}{{ end }}
{{- if .Arm }}v{{ .Arm }}{{ end }}
format_overrides:
- goos: windows
format: zip
changelog:
groups:
- title: 'New Features'
regexp: "^.*feat[(\\w)]*:+.*$"
order: 0
- title: 'Bug fixes'
regexp: "^.*fix[(\\w)]*:+.*$"
order: 1
- title: 'Documentation updates'
regexp: "^.*docs[(\\w)]*:+.*$"
order: 2
- title: 'Other'
order: 999
release:
prerelease: auto
mode: append

View File

@@ -1,12 +0,0 @@
# Default state for all rules
default: true
# MD013/line-length - Line length
MD013:
line_length: 1024
# MD033/no-inline-html - Inline HTML
MD033: false
# MD041/first-line-heading/first-line-h1 - First line in a file should be a top-level heading
MD041: false

View File

@@ -1,20 +0,0 @@
---
APPLY_FIXES: none
DISABLE:
- ACTION
- BASH
- COPYPASTE
- DOCKERFILE
- GO
- JAVASCRIPT
- SPELL
DISABLE_LINTERS:
- YAML_YAMLLINT
- MARKDOWN_MARKDOWN_TABLE_FORMATTER
- MARKDOWN_MARKDOWN_LINK_CHECK
- REPOSITORY_CHECKOV
- REPOSITORY_TRIVY
FILTER_REGEX_EXCLUDE: (.*testdata/*|install.sh|pkg/container/docker_cli.go|pkg/container/DOCKER_LICENSE|VERSION)
MARKDOWN_MARKDOWNLINT_CONFIG_FILE: .markdownlint.yml
PARALLEL: false
PRINT_ALPACA: false

View File

@@ -1,93 +0,0 @@
pull_request_rules:
- name: warn on conflicts
conditions:
- -draft
- -closed
- -merged
- conflict
actions:
comment:
message: '@{{author}} this pull request is now in conflict 😩'
label:
add:
- conflict
- name: remove conflict label if not needed
conditions:
- -conflict
actions:
label:
remove:
- conflict
- name: warn on needs-work
conditions:
- -draft
- -closed
- -merged
- or:
- check-failure=lint
- check-failure=test-linux
- check-failure=codecov/patch
- check-failure=codecov/project
- check-failure=snapshot
actions:
comment:
message: '@{{author}} this pull request has failed checks 🛠'
label:
add:
- needs-work
- name: remove needs-work label if not needed
conditions:
- check-success=lint
- check-success=test-linux
- check-success=codecov/patch
- check-success=codecov/project
- check-success=snapshot
actions:
label:
remove:
- needs-work
- name: Automatic maintainer assignment
conditions:
- '-approved-reviews-by=@nektos/act-maintainers'
- -draft
- -merged
- -closed
- -conflict
- check-success=lint
- check-success=test-linux
- check-success=codecov/patch
- check-success=codecov/project
- check-success=snapshot
actions:
request_reviews:
teams:
- '@nektos/act-maintainers'
- name: Automatic merge on approval
conditions:
- '#changes-requested-reviews-by=0'
- or:
- 'approved-reviews-by=@nektos/act-committers'
- 'author~=^dependabot(|-preview)\[bot\]$'
- and:
- 'approved-reviews-by=@nektos/act-maintainers'
- '#approved-reviews-by>=2'
- -draft
- -merged
- -closed
- check-success=lint
- check-success=test-linux
- check-success=codecov/patch
- check-success=codecov/project
- check-success=snapshot
actions:
queue:
method: squash
name: default
queue_rules:
- name: default
conditions:
- check-success=lint
- check-success=test-linux
- check-success=codecov/patch
- check-success=codecov/project
- check-success=snapshot

View File

@@ -1,2 +0,0 @@
**/testdata
pkg/runner/res

View File

@@ -1,7 +0,0 @@
overrides:
- files: '*.yml'
options:
singleQuote: true
- files: '*.json'
options:
singleQuote: false

View File

@@ -1,9 +0,0 @@
{
"recommendations": [
"editorconfig.editorconfig",
"golang.go",
"davidanson.vscode-markdownlint",
"esbenp.prettier-vscode",
"redhat.vscode-yaml"
]
}

14
.vscode/settings.json vendored
View File

@@ -1,14 +0,0 @@
{
"go.lintTool": "golangci-lint",
"go.lintFlags": ["--fix"],
"go.testTimeout": "300s",
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[markdown]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[yaml]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}

10
AGENTS.md Normal file
View File

@@ -0,0 +1,10 @@
- Use `make help` to find available development targets
- Run `make fmt` to format `.go` files, and run `make lint-go` to lint them
- Run `make tidy` after any `go.mod` changes
- Run single go unit tests with `go test -run '^TestName$' ./modulepath/`
- Add the current year into the copyright header of new `.go` files
- Ensure no trailing whitespace in edited files
- Never force-push, amend, or squash unless asked. Use new commits and normal push for pull request updates
- Preserve existing code comments, do not remove or rewrite comments that are still relevant
- Include authorship attribution in issue and pull request comments
- Add `Co-Authored-By` lines to all commits, indicating name and model used

1
CLAUDE.md Normal file
View File

@@ -0,0 +1 @@
@AGENTS.md

View File

@@ -1 +0,0 @@
* @nektos/act-maintainers

View File

@@ -1,69 +0,0 @@
# Contributing to Act
Help wanted! We'd love your contributions to Act. Please review the following guidelines before contributing. Also, feel free to propose changes to these guidelines by updating this file and submitting a pull request.
- [I have a question...](#questions)
- [I found a bug...](#bugs)
- [I have a feature request...](#features)
- [I have a contribution to share...](#process)
## <a id="questions"></a> Have a Question?
Please don't open a GitHub issue for questions about how to use `act`, as the goal is to use issues for managing bugs and feature requests. Issues that are related to general support will be closed and redirected to our gitter room.
For all support related questions, please ask the question in our gitter room: [nektos/act](https://gitter.im/nektos/act).
## <a id="bugs"></a> Found a Bug?
If you've identified a bug in `act`, please [submit an issue](#issue) to our GitHub repo: [nektos/act](https://github.com/nektos/act/issues/new). Please also feel free to submit a [Pull Request](#pr) with a fix for the bug!
## <a id="features"></a> Have a Feature Request?
All feature requests should start with [submitting an issue](#issue) documenting the user story and acceptance criteria. Again, feel free to submit a [Pull Request](#pr) with a proposed implementation of the feature.
## <a id="process"></a> Ready to Contribute
### <a id="issue"></a> Create an issue
Before submitting a new issue, please search the issues to make sure there isn't a similar issue doesn't already exist.
Assuming no existing issues exist, please ensure you include required information when submitting the issue to ensure we can quickly reproduce your issue.
We may have additional questions and will communicate through the GitHub issue, so please respond back to our questions to help reproduce and resolve the issue as quickly as possible.
New issues can be created with in our [GitHub repo](https://github.com/nektos/act/issues/new).
### <a id="pr"></a>Pull Requests
Pull requests should target the `master` branch. Please also reference the issue from the description of the pull request using [special keyword syntax](https://help.github.com/articles/closing-issues-via-commit-messages/) to auto close the issue when the PR is merged. For example, include the phrase `fixes #14` in the PR description to have issue #14 auto close. Please send documentation updates for the [act user guide](https://nektosact.com) to [nektos/act-docs](https://github.com/nektos/act-docs).
### <a id="style"></a> Styleguide
When submitting code, please make every effort to follow existing conventions and style in order to keep the code as readable as possible. Here are a few points to keep in mind:
- Please run `go fmt ./...` before committing to ensure code aligns with go standards.
- We use [`golangci-lint`](https://golangci-lint.run/) for linting Go code, run `golangci-lint run --fix` before submitting PR. Editors such as Visual Studio Code or JetBrains IntelliJ; with Go support plugin will offer `golangci-lint` automatically.
- There are additional linters and formatters for files such as Markdown documents or YAML/JSON:
- Please refer to the [Makefile](Makefile) or [`lint` job in our workflow](.github/workflows/checks.yml) to see how to those linters/formatters work.
- You can lint codebase by running `go run main.go -j lint --env RUN_LOCAL=true` or `act -j lint --env RUN_LOCAL=true`
- In `Makefile`, there are tools that require `npx` which is shipped with `nodejs`.
- Our `Makefile` exports `GITHUB_TOKEN` from `~/.config/github/token`, you have been warned.
- You can run `make pr` to cleanup dependencies, format/lint code and run tests.
- All dependencies must be defined in the `go.mod` file.
- Advanced IDEs and code editors (like VSCode) will take care of that, but to be sure, run `go mod tidy` to validate dependencies.
- For details on the approved style, check out [Effective Go](https://golang.org/doc/effective_go.html).
- Before running tests, please be aware that they are multi-architecture so for them to not fail, you need to run `docker run --privileged --rm tonistiigi/binfmt --install amd64,arm64` before ([more info available in #765](https://github.com/nektos/act/issues/765)).
Also, consider the original design principles:
- **Polyglot** - There will be no prescribed language or framework for developing the microservices. The only requirement will be that the service will be run inside a container and exposed via an HTTP endpoint.
- **Cloud Provider** - At this point, the tool will assume AWS for the cloud provider and will not be written in a cloud agnostic manner. However, this does not preclude refactoring to add support for other providers at a later time.
- **Declarative** - All resource administration will be handled in a declarative vs. imperative manner. A file will be used to declared the desired state of the resources and the tool will simply assert the actual state matches the desired state. The tool will accomplish this by generating CloudFormation templates.
- **Stateless** - The tool will not maintain its own state. Rather, it will rely on the CloudFormation stacks to determine the state of the platform.
- **Secure** - All security will be managed by AWS IAM credentials. No additional authentication or authorization mechanisms will be introduced.
### License
By contributing your code, you agree to license your contribution under the terms of the [MIT License](LICENSE).
All files are released with the MIT license.

80
Dockerfile Normal file
View File

@@ -0,0 +1,80 @@
### BUILDER STAGE
#
#
FROM golang:1.26-alpine3.23 AS builder
# Do not remove `git` here, it is required for getting runner version when executing `make build`
RUN apk add --no-cache make git
ARG GOPROXY
ENV GOPROXY=${GOPROXY:-}
COPY . /opt/src/runner
WORKDIR /opt/src/runner
RUN make clean && make build
### DIND VARIANT
#
#
FROM docker:29.5.2-dind AS dind
ARG VERSION=dev
LABEL org.opencontainers.image.source="https://gitea.com/gitea/runner"
LABEL org.opencontainers.image.version="${VERSION}"
RUN apk add --no-cache s6 bash git tzdata
COPY --from=builder /opt/src/runner/gitea-runner /usr/local/bin/gitea-runner
COPY scripts/run.sh /usr/local/bin/run.sh
COPY scripts/s6 /etc/s6
VOLUME /data
ENTRYPOINT ["s6-svscan","/etc/s6"]
### DIND-ROOTLESS VARIANT
#
#
FROM docker:29.5.2-dind-rootless AS dind-rootless
ARG VERSION=dev
LABEL org.opencontainers.image.source="https://gitea.com/gitea/runner"
LABEL org.opencontainers.image.version="${VERSION}"
USER root
RUN apk add --no-cache s6 bash git tzdata
COPY --from=builder /opt/src/runner/gitea-runner /usr/local/bin/gitea-runner
COPY scripts/run.sh /usr/local/bin/run.sh
COPY scripts/s6 /etc/s6
VOLUME /data
RUN mkdir -p /data && chown -R rootless:rootless /etc/s6 /data
ENV DOCKER_HOST=unix:///run/user/1000/docker.sock
USER rootless
ENTRYPOINT ["s6-svscan","/etc/s6"]
### BASIC VARIANT
#
#
FROM alpine:3.23 AS basic
ARG VERSION=dev
LABEL org.opencontainers.image.source="https://gitea.com/gitea/runner"
LABEL org.opencontainers.image.version="${VERSION}"
RUN apk add --no-cache tini bash git tzdata
COPY --from=builder /opt/src/runner/gitea-runner /usr/local/bin/gitea-runner
COPY scripts/run.sh /usr/local/bin/run.sh
VOLUME /data
ENTRYPOINT ["/sbin/tini","--","run.sh"]

View File

@@ -1,67 +0,0 @@
# List of Docker images for `act`
**Warning:** Below badges with size for each image are displaying size of **compressed image size in registry. After pulling the image, size can be drastically different due to Docker uncompressing the image layers.**
## Images based on [`buildpack-deps`][hub/_/buildpack-deps]
**Note 1: `node` images are based on Debian root filesystem, while it is extremely similar to Ubuntu, there might be some differences**
**Note 2: `node` `-slim` images don't have `python` installed, if you want to use actions or software that is depending on `python`, you need to specify image manually**
| Image | Size |
| ------------------------------------- | ---------------------------------------------------------- |
| [`node:16-bullseye`][hub/_/node] | ![`bullseye-size`][hub/_/node/16-bullseye/size] |
| [`node:16-bullseye-slim`][hub/_/node] | ![`micro-bullseye-size`][hub/_/node/16-bullseye-slim/size] |
| [`node:16-buster`][hub/_/node] | ![`buster-size`][hub/_/node/16-buster/size] |
| [`node:16-buster-slim`][hub/_/node] | ![`micro-buster-size`][hub/_/node/16-buster-slim/size] |
**Note: `catthehacker/ubuntu` images are based on Ubuntu root filesystem**
| Image | GitHub Repository |
| ------------------------------------------------------------ | ------------------------------------------------------------- |
| [`catthehacker/ubuntu:act-latest`][ghcr/catthehacker/ubuntu] | [`catthehacker/docker-images`][gh/catthehacker/docker_images] |
| [`catthehacker/ubuntu:act-22.04`][ghcr/catthehacker/ubuntu] | [`catthehacker/docker-images`][gh/catthehacker/docker_images] |
| [`catthehacker/ubuntu:act-20.04`][ghcr/catthehacker/ubuntu] | [`catthehacker/docker-images`][gh/catthehacker/docker_images] |
| [`catthehacker/ubuntu:act-18.04`][ghcr/catthehacker/ubuntu] | [`catthehacker/docker-images`][gh/catthehacker/docker_images] |
## Images based on [`actions/virtual-environments`][gh/actions/virtual-environments]
**Note: `nektos/act-environments-ubuntu` have been last updated in February, 2020. It's recommended to update the image manually after `docker pull` if you decide to to use it.**
| Image | Size | GitHub Repository |
| --------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------- |
| [`nektos/act-environments-ubuntu:18.04`][hub/nektos/act-environments-ubuntu] | ![`nektos:18.04`][hub/nektos/act-environments-ubuntu/18.04/size] | [`nektos/act-environments`][gh/nektos/act-environments] |
| [`nektos/act-environments-ubuntu:18.04-lite`][hub/nektos/act-environments-ubuntu] | ![`nektos:18.04-lite`][hub/nektos/act-environments-ubuntu/18.04-lite/size] | [`nektos/act-environments`][gh/nektos/act-environments] |
| [`nektos/act-environments-ubuntu:18.04-full`][hub/nektos/act-environments-ubuntu] | ![`nektos:18.04-full`][hub/nektos/act-environments-ubuntu/18.04-full/size] | [`nektos/act-environments`][gh/nektos/act-environments] |
| Image | GitHub Repository |
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| [`catthehacker/ubuntu:full-latest`][ghcr/catthehacker/ubuntu] | [`catthehacker/virtual-environments-fork`][gh/catthehacker/virtual-environments-fork] |
| [`catthehacker/ubuntu:full-20.04`][ghcr/catthehacker/ubuntu] | [`catthehacker/virtual-environments-fork`][gh/catthehacker/virtual-environments-fork] |
| [`catthehacker/ubuntu:full-18.04`][ghcr/catthehacker/ubuntu] | [`catthehacker/virtual-environments-fork`][gh/catthehacker/virtual-environments-fork] |
Feel free to make a pull request with your image added here
[hub/_/buildpack-deps]: https://hub.docker.com/_/buildpack-deps
[hub/_/node]: https://hub.docker.com/r/_/node
[hub/_/node/16-bullseye/size]: https://img.shields.io/docker/image-size/_/node/16-bullseye
[hub/_/node/16-bullseye-slim/size]: https://img.shields.io/docker/image-size/_/node/16-bullseye-slim
[hub/_/node/16-buster/size]: https://img.shields.io/docker/image-size/_/node/16-buster
[hub/_/node/16-buster-slim/size]: https://img.shields.io/docker/image-size/_/node/16-buster-slim
[ghcr/catthehacker/ubuntu]: https://github.com/catthehacker/docker_images/pkgs/container/ubuntu
[hub/nektos/act-environments-ubuntu]: https://hub.docker.com/r/nektos/act-environments-ubuntu
[hub/nektos/act-environments-ubuntu/18.04/size]: https://img.shields.io/docker/image-size/nektos/act-environments-ubuntu/18.04
[hub/nektos/act-environments-ubuntu/18.04-lite/size]: https://img.shields.io/docker/image-size/nektos/act-environments-ubuntu/18.04-lite
[hub/nektos/act-environments-ubuntu/18.04-full/size]: https://img.shields.io/docker/image-size/nektos/act-environments-ubuntu/18.04-full
<!--
[hub/<username>/<image>]: https://hub.docker.com/r/[username]/[image]
[hub/<username>/<image>/<tag>/size]: https://img.shields.io/docker/image-size/[username]/[image]/[tag]
-->
<!-- GitHub repository links -->
[gh/nektos/act-environments]: https://github.com/nektos/act-environments
[gh/actions/virtual-environments]: https://github.com/actions/virtual-environments
[gh/catthehacker/docker_images]: https://github.com/catthehacker/docker_images
[gh/catthehacker/virtual-environments-fork]: https://github.com/catthehacker/virtual-environments-fork

11
LICENSE
View File

@@ -1,7 +1,4 @@
MIT License
Copyright (c) 2022 The Gitea Authors
Copyright (c) 2019
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -10,13 +7,13 @@ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

274
Makefile
View File

@@ -1,113 +1,217 @@
PREFIX ?= /usr/local
VERSION ?= $(shell git describe --tags --dirty --always | sed -e 's/^v//')
IS_SNAPSHOT = $(if $(findstring -, $(VERSION)),true,false)
MAJOR_VERSION = $(word 1, $(subst ., ,$(VERSION)))
MINOR_VERSION = $(word 2, $(subst ., ,$(VERSION)))
PATCH_VERSION = $(word 3, $(subst ., ,$(word 1,$(subst -, , $(VERSION)))))
NEW_VERSION ?= $(MAJOR_VERSION).$(MINOR_VERSION).$(shell echo $$(( $(PATCH_VERSION) + 1)) )
DIST := dist
EXECUTABLE := gitea-runner
DIST_DIRS := $(DIST)/binaries $(DIST)/release
GO ?= go
SHASUM ?= shasum -a 256
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
XGO_VERSION := go-1.26.x
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.10
fix = false
ifeq (true,$(fix))
FIX = --fix
LINUX_ARCHS ?= linux/amd64,linux/arm64
DARWIN_ARCHS ?= darwin-12/amd64,darwin-12/arm64
WINDOWS_ARCHS ?= windows/amd64
GOFILES := $(shell find . -type f -name "*.go" -o -name "go.mod" ! -name "generated.*")
DOCKER_IMAGE ?= gitea/runner
DOCKER_TAG ?= nightly
DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)
DOCKER_ROOTLESS_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)-dind-rootless
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.12.2
GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.3.0
STATIC ?=
EXTLDFLAGS ?=
ifneq ($(STATIC),)
EXTLDFLAGS = -extldflags "-static"
endif
ACT ?= go run main.go
ifeq ($(HAS_GO), GO)
GOPATH ?= $(shell $(GO) env GOPATH)
export PATH := $(GOPATH)/bin:$(PATH)
HAS_TOKEN = $(if $(test -e ~/.config/github/token),true,false)
ifeq (true,$(HAS_TOKEN))
export GITHUB_TOKEN := $(shell cat ~/.config/github/token)
CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766
CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
endif
.PHONY: pr
pr: tidy format-all lint test
ifeq ($(OS), Windows_NT)
GOFLAGS := -v -buildmode=exe
EXECUTABLE ?= $(EXECUTABLE).exe
else ifeq ($(OS), Windows)
GOFLAGS := -v -buildmode=exe
EXECUTABLE ?= $(EXECUTABLE).exe
else
GOFLAGS := -v
EXECUTABLE ?= $(EXECUTABLE)
endif
.PHONY: build
build:
go build -ldflags "-X main.version=$(VERSION)" -o dist/local/act main.go
STORED_VERSION_FILE := VERSION
.PHONY: format
format:
go fmt ./...
ifneq ($(DRONE_TAG),)
VERSION ?= $(subst v,,$(DRONE_TAG))
RELASE_VERSION ?= $(VERSION)
else
ifneq ($(DRONE_BRANCH),)
VERSION ?= $(subst release/v,,$(DRONE_BRANCH))
else
VERSION ?= main
endif
.PHONY: format-all
format-all:
go fmt ./...
npx prettier --write .
STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null)
ifneq ($(STORED_VERSION),)
RELASE_VERSION ?= $(STORED_VERSION)
else
RELASE_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
endif
endif
.PHONY: test
test:
go test ./...
$(ACT)
TAGS ?=
LDFLAGS ?= -X "gitea.com/gitea/runner/internal/pkg/ver.version=v$(RELASE_VERSION)"
.PHONY: lint-go
lint-go:
golangci-lint run $(FIX)
.PHONY: all
all: build
.PHONY: lint-js
lint-js:
npx standard $(FIX)
.PHONY: help
help: Makefile ## print Makefile help information.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m[TARGETS] default target: build\033[0m\n\n\033[35mTargets:\033[0m\n"} /^[0-9A-Za-z._-]+:.*?##/ { printf " \033[36m%-45s\033[0m %s\n", $$1, $$2 }' Makefile
.PHONY: lint-md
lint-md:
npx markdownlint . $(FIX)
.PHONY: fmt
fmt: ## format the Go code
$(GO) run $(GOLANGCI_LINT_PACKAGE) fmt
.PHONY: lint-rest
lint-rest:
docker run --rm -it \
-v $(PWD):/tmp/lint \
-e GITHUB_STATUS_REPORTER=false \
-e GITHUB_COMMENT_REPORTER=false \
megalinter/megalinter-go:v5
.PHONY: go-check
go-check:
$(eval MIN_GO_VERSION_STR := $(shell grep -Eo '^go\s+[0-9]+\.[0-9]+' go.mod | cut -d' ' -f2))
$(eval MIN_GO_VERSION := $(shell printf "%03d%03d" $(shell echo '$(MIN_GO_VERSION_STR)' | tr '.' ' ')))
$(eval GO_VERSION := $(shell printf "%03d%03d" $(shell $(GO) version | grep -Eo '[0-9]+\.[0-9]+' | tr '.' ' ');))
@if [ "$(GO_VERSION)" -lt "$(MIN_GO_VERSION)" ]; then \
echo "Gitea Runner requires Go $(MIN_GO_VERSION_STR) or greater to build. You can get it at https://go.dev/dl/"; \
exit 1; \
fi
.PHONY: fmt-check
fmt-check: fmt
@diff=$$(git diff --color=always); \
if [ -n "$$diff" ]; then \
echo "Please run 'make fmt' and commit the result:"; \
printf "%s" "$${diff}"; \
exit 1; \
fi
.PHONY: deps-tools
deps-tools: ## install tool dependencies
$(GO) install $(GOLANGCI_LINT_PACKAGE) & \
$(GO) install $(GXZ_PACKAGE) & \
$(GO) install $(XGO_PACKAGE) & \
$(GO) install $(GOVULNCHECK_PACKAGE) & \
wait
.PHONY: lint
lint: lint-go lint-rest
lint: lint-go ## lint everything
.PHONY: lint-fix
lint-fix: lint-md lint-go
.PHONY: lint-go
lint-go: ## lint go files
$(GO) run $(GOLANGCI_LINT_PACKAGE) run
.PHONY: fix
fix:
make lint-fix fix=true
.PHONY: lint-go-fix
lint-go-fix: ## lint go files and fix issues
$(GO) run $(GOLANGCI_LINT_PACKAGE) run --fix
.PHONY: lint-pr-title
lint-pr-title: ## lint PR title against Conventional Commits (set PR_TITLE=...)
@node ./tools/lint-pr-title.ts
.PHONY: security-check
security-check: deps-tools
GOEXPERIMENT= $(GO) run $(GOVULNCHECK_PACKAGE) -show color ./... || true
.PHONY: tidy
tidy:
go mod tidy
tidy: ## run go mod tidy
$(GO) mod tidy
.PHONY: tidy-check
tidy-check: tidy
@diff=$$(git diff --color=always -- go.mod go.sum); \
if [ -n "$$diff" ]; then \
echo "Please run 'make tidy' and commit the result:"; \
printf "%s" "$${diff}"; \
exit 1; \
fi
.PHONY: test
test: fmt-check security-check ## test everything (integration tests self-skip without docker/network)
@$(GO) test -race -timeout 20m -v -cover -coverprofile coverage.txt ./... && echo "\n==>\033[32m Ok\033[m\n" || exit 1
.PHONY: test-dind
test-dind: ## run the daemon-facing tests against the built dind image (TARGET=dind|dind-rootless)
@./scripts/test-dind.sh $(TARGET)
.PHONY: install
install: build
@cp dist/local/act $(PREFIX)/bin/act
@chmod 755 $(PREFIX)/bin/act
@act --version
install: $(GOFILES) ## install the runner binary via `go install`
$(GO) install -v -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)'
.PHONY: installer
installer:
@GO111MODULE=off go get github.com/goreleaser/godownloader
godownloader -r nektos/act -o install.sh
.PHONY: build
build: go-check $(EXECUTABLE) ## build the runner binary
.PHONY: promote
promote:
@git fetch --tags
@echo "VERSION:$(VERSION) IS_SNAPSHOT:$(IS_SNAPSHOT) NEW_VERSION:$(NEW_VERSION)"
ifeq (false,$(IS_SNAPSHOT))
@echo "Unable to promote a non-snapshot"
@exit 1
$(EXECUTABLE): $(GOFILES)
$(GO) build -v -tags '$(TAGS)' -ldflags '-s -w $(EXTLDFLAGS) $(LDFLAGS)' -o $@
.PHONY: deps-backend
deps-backend: ## install backend dependencies
$(GO) mod download
.PHONY: release
release: release-windows release-linux release-darwin release-copy release-compress release-check ## build release artifacts
$(DIST_DIRS):
mkdir -p $(DIST_DIRS)
.PHONY: release-windows
release-windows: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -buildmode exe -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(WINDOWS_ARCHS)' -out $(EXECUTABLE)-$(VERSION) .
ifeq ($(CI),true)
cp -r /build/* $(DIST)/binaries/
endif
ifneq ($(shell git status -s),)
@echo "Unable to promote a dirty workspace"
@exit 1
.PHONY: release-linux
release-linux: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w -linkmode external -extldflags "-static" $(LDFLAGS)' -targets '$(LINUX_ARCHS)' -out $(EXECUTABLE)-$(VERSION) .
ifeq ($(CI),true)
cp -r /build/* $(DIST)/binaries/
endif
echo -n $(NEW_VERSION) > VERSION
git add VERSION
git commit -m "chore: bump VERSION to $(NEW_VERSION)"
git tag -a -m "releasing v$(NEW_VERSION)" v$(NEW_VERSION)
git push origin master
git push origin v$(NEW_VERSION)
.PHONY: snapshot
snapshot:
goreleaser build \
--clean \
--single-target \
--snapshot
.PHONY: release-darwin
release-darwin: | $(DIST_DIRS)
CGO_CFLAGS="$(CGO_CFLAGS)" $(GO) run $(XGO_PACKAGE) -go $(XGO_VERSION) -dest $(DIST)/binaries -tags 'netgo osusergo $(TAGS)' -ldflags '-s -w $(LDFLAGS)' -targets '$(DARWIN_ARCHS)' -out $(EXECUTABLE)-$(VERSION) .
ifeq ($(CI),true)
cp -r /build/* $(DIST)/binaries/
endif
.PHONY: clean all
.PHONY: release-copy
release-copy: | $(DIST_DIRS)
cd $(DIST); for file in `find . -type f -name "*"`; do cp $${file} ./release/; done;
.PHONY: release-check
release-check: | $(DIST_DIRS)
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "checksumming $${file}" && $(SHASUM) `echo $${file} | sed 's/^..//'` > $${file}.sha256; done;
.PHONY: release-compress
release-compress: | $(DIST_DIRS)
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && $(GO) run $(GXZ_PACKAGE) -k -9 $${file}; done;
.PHONY: docker
docker: ## build the docker image
if ! docker buildx version >/dev/null 2>&1; then \
ARG_DISABLE_CONTENT_TRUST=--disable-content-trust=false; \
fi; \
docker build $${ARG_DISABLE_CONTENT_TRUST} -t $(DOCKER_REF) .
.PHONY: clean
clean: ## delete binary and coverage files
$(GO) clean -x -i ./...
rm -rf coverage.txt $(EXECUTABLE) $(DIST)
.PHONY: version
version: ## print the version
@echo $(VERSION)

527
README.md
View File

@@ -1,504 +1,143 @@
## Forking rules
# Gitea Runner
This is a custom fork of [nektos/act](https://github.com/nektos/act/), for the purpose of serving [act_runner](https://gitea.com/gitea/act_runner).
## Installation
It cannot be used as command line tool anymore, but only as a library.
### Prerequisites
It's a soft fork, which means that it will tracking the latest release of nektos/act.
Docker Engine Community version is required for docker mode. To install Docker CE, follow the official [install instructions](https://docs.docker.com/engine/install/).
Branches:
### Download pre-built binary
- `main`: default branch, contains custom changes, based on the latest release(not the latest of the master branch) of nektos/act.
- `nektos/master`: mirror for the master branch of nektos/act.
Visit [here](https://dl.gitea.com/gitea-runner/) and download the right version for your platform.
Tags:
- `nektos/vX.Y.Z`: mirror for `vX.Y.Z` of [nektos/act](https://github.com/nektos/act/).
- `vX.YZ.*`: based on `nektos/vX.Y.Z`, contains custom changes.
- Examples:
- `nektos/v0.2.23` -> `v0.223.*`
- `nektos/v0.3.1` -> `v0.301.*`, not ~~`v0.31.*`~~
- `nektos/v0.10.1` -> `v0.1001.*`, not ~~`v0.101.*`~~
- `nektos/v0.3.100` -> not ~~`v0.3100.*`~~, I don't think it's really going to happen, if it does, we can find a way to handle it.
---
![act-logo](https://github.com/nektos/act/wiki/img/logo-150.png)
# Overview [![push](https://github.com/nektos/act/workflows/push/badge.svg?branch=master&event=push)](https://github.com/nektos/act/actions) [![Join the chat at https://gitter.im/nektos/act](https://badges.gitter.im/nektos/act.svg)](https://gitter.im/nektos/act?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Go Report Card](https://goreportcard.com/badge/github.com/nektos/act)](https://goreportcard.com/report/github.com/nektos/act) [![awesome-runners](https://img.shields.io/badge/listed%20on-awesome--runners-blue.svg)](https://github.com/jonico/awesome-runners)
> "Think globally, `act` locally"
Run your [GitHub Actions](https://developer.github.com/actions/) locally! Why would you want to do this? Two reasons:
- **Fast Feedback** - Rather than having to commit/push every time you want to test out the changes you are making to your `.github/workflows/` files (or for any changes to embedded GitHub actions), you can use `act` to run the actions locally. The [environment variables](https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables) and [filesystem](https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners#filesystems-on-github-hosted-runners) are all configured to match what GitHub provides.
- **Local Task Runner** - I love [make](<https://en.wikipedia.org/wiki/Make_(software)>). However, I also hate repeating myself. With `act`, you can use the GitHub Actions defined in your `.github/workflows/` to replace your `Makefile`!
# How Does It Work?
When you run `act` it reads in your GitHub Actions from `.github/workflows/` and determines the set of actions that need to be run. It uses the Docker API to either pull or build the necessary images, as defined in your workflow files and finally determines the execution path based on the dependencies that were defined. Once it has the execution path, it then uses the Docker API to run containers for each action based on the images prepared earlier. The [environment variables](https://help.github.com/en/actions/configuring-and-managing-workflows/using-environment-variables#default-environment-variables) and [filesystem](https://docs.github.com/en/actions/using-github-hosted-runners/about-github-hosted-runners#file-systems) are all configured to match what GitHub provides.
Let's see it in action with a [sample repo](https://github.com/cplee/github-actions-demo)!
![Demo](https://github.com/nektos/act/wiki/quickstart/act-quickstart-2.gif)
# Act User Guide
Please look at the [act user guide](https://nektosact.com) for more documentation.
# Installation
## Necessary prerequisites for running `act`
`act` depends on `docker` to run workflows.
If you are using macOS, please be sure to follow the steps outlined in [Docker Docs for how to install Docker Desktop for Mac](https://docs.docker.com/docker-for-mac/install/).
If you are using Windows, please follow steps for [installing Docker Desktop on Windows](https://docs.docker.com/docker-for-windows/install/).
If you are using Linux, you will need to [install Docker Engine](https://docs.docker.com/engine/install/).
`act` is currently not supported with `podman` or other container backends (it might work, but it's not guaranteed). Please see [#303](https://github.com/nektos/act/issues/303) for updates.
## Installation through package managers
### [Homebrew](https://brew.sh/) (Linux/macOS)
[![homebrew version](https://img.shields.io/homebrew/v/act)](https://github.com/Homebrew/homebrew-core/blob/master/Formula/act.rb)
```shell
brew install act
```
or if you want to install version based on latest commit, you can run below (it requires compiler to be installed but Homebrew will suggest you how to install it, if you don't have it):
```shell
brew install act --HEAD
```
### [MacPorts](https://www.macports.org) (macOS)
[![MacPorts package](https://repology.org/badge/version-for-repo/macports/act-run-github-actions.svg)](https://repology.org/project/act-run-github-actions/versions)
```shell
sudo port install act
```
### [Chocolatey](https://chocolatey.org/) (Windows)
[![choco-shield](https://img.shields.io/chocolatey/v/act-cli)](https://community.chocolatey.org/packages/act-cli)
```shell
choco install act-cli
```
### [Scoop](https://scoop.sh/) (Windows)
[![scoop-shield](https://img.shields.io/scoop/v/act)](https://github.com/ScoopInstaller/Main/blob/master/bucket/act.json)
```shell
scoop install act
```
### [Winget](https://learn.microsoft.com/en-us/windows/package-manager/) (Windows)
[![Winget package](https://repology.org/badge/version-for-repo/winget/act-run-github-actions.svg)](https://repology.org/project/act-run-github-actions/versions)
```shell
winget install nektos.act
```
### [AUR](https://aur.archlinux.org/packages/act/) (Linux)
[![aur-shield](https://img.shields.io/aur/version/act)](https://aur.archlinux.org/packages/act/)
```shell
yay -Syu act
```
### [COPR](https://copr.fedorainfracloud.org/coprs/rubemlrm/act-cli/) (Linux)
```shell
dnf copr enable rubemlrm/act-cli
dnf install act-cli
```
### [Nix](https://nixos.org) (Linux/macOS)
[Nix recipe](https://github.com/NixOS/nixpkgs/blob/master/pkgs/development/tools/misc/act/default.nix)
Global install:
```sh
nix-env -iA nixpkgs.act
```
or through `nix-shell`:
```sh
nix-shell -p act
```
Using the latest [Nix command](https://nixos.wiki/wiki/Nix_command), you can run directly :
```sh
nix run nixpkgs#act
```
## Installation as GitHub CLI extension
Act can be installed as a [GitHub CLI](https://cli.github.com/) extension:
```sh
gh extension install https://github.com/nektos/gh-act
```
## Other install options
### Bash script
Run this command in your terminal:
```shell
curl -s https://raw.githubusercontent.com/nektos/act/master/install.sh | sudo bash
```
### Manual download
Download the [latest release](https://github.com/nektos/act/releases/latest) and add the path to your binary into your PATH.
# Example commands
```sh
# Command structure:
act [<event>] [options]
If no event name passed, will default to "on: push"
If actions handles only one event it will be used as default instead of "on: push"
# List all actions for all events:
act -l
# List the actions for a specific event:
act workflow_dispatch -l
# List the actions for a specific job:
act -j test -l
# Run the default (`push`) event:
act
# Run a specific event:
act pull_request
# Run a specific job:
act -j test
# Collect artifacts to the /tmp/artifacts folder:
act --artifact-server-path /tmp/artifacts
# Run a job in a specific workflow (useful if you have duplicate job names)
act -j lint -W .github/workflows/checks.yml
# Run in dry-run mode:
act -n
# Enable verbose-logging (can be used with any of the above commands)
act -v
```
## First `act` run
When running `act` for the first time, it will ask you to choose image to be used as default.
It will save that information to `~/.actrc`, please refer to [Configuration](#configuration) for more information about `.actrc` and to [Runners](#runners) for information about used/available Docker images.
## `GITHUB_TOKEN`
GitHub [automatically provides](https://docs.github.com/en/actions/security-guides/automatic-token-authentication#about-the-github_token-secret) a `GITHUB_TOKEN` secret when running workflows inside GitHub.
If your workflow depends on this token, you need to create a [personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) and pass it to `act` as a secret:
### Build from source
```bash
act -s GITHUB_TOKEN=[insert token or leave blank and omit equals for secure input]
make build
```
If [GitHub CLI](https://cli.github.com/) is installed, the [`gh auth token`](https://cli.github.com/manual/gh_auth_token) command can be used to automatically pass the token to act
### Build a docker image
```bash
act -s GITHUB_TOKEN="$(gh auth token)"
make docker
```
**WARNING**: `GITHUB_TOKEN` will be logged in shell history if not inserted through secure input or (depending on your shell config) the command is prefixed with a whitespace.
## Quickstart
# Known Issues
Actions are disabled by default, so you need to add the following to the configuration file of your Gitea instance to enable it:
## Services
Services are not currently supported but are being worked on. See: [#173](https://github.com/nektos/act/issues/173)
## `MODULE_NOT_FOUND`
A `MODULE_NOT_FOUND` during `docker cp` command [#228](https://github.com/nektos/act/issues/228) can happen if you are relying on local changes that have not been pushed. This can get triggered if the action is using a path, like:
```yaml
- name: test action locally
uses: ./
```ini
[actions]
ENABLED=true
```
In this case, you _must_ use `actions/checkout@v2` with a path that _has the same name as your repository_. If your repository is called _my-action_, then your checkout step would look like:
```yaml
steps:
- name: Checkout
uses: actions/checkout@v2
with:
path: "my-action"
```
If the `path:` value doesn't match the name of the repository, a `MODULE_NOT_FOUND` will be thrown.
## `docker context` support
The current `docker context` isn't respected ([#583](https://github.com/nektos/act/issues/583)).
You can work around this by setting `DOCKER_HOST` before running `act`, with e.g:
### Register
```bash
export DOCKER_HOST=$(docker context inspect --format '{{.Endpoints.docker.Host}}')
./gitea-runner register
```
# Runners
And you will be asked to input:
GitHub Actions offers managed [virtual environments](https://help.github.com/en/actions/reference/virtual-environments-for-github-hosted-runners) for running workflows. In order for `act` to run your workflows locally, it must run a container for the runner defined in your workflow file. Here are the images that `act` uses for each runner type and size:
1. Gitea instance URL, like `http://192.168.8.8:3000/`. You should use your gitea instance ROOT_URL as the instance argument
and you should not use `localhost` or `127.0.0.1` as instance IP;
2. Runner token, you can get it from `http://192.168.8.8:3000/admin/actions/runners`;
3. Runner name, you can just leave it blank;
4. Runner labels, you can just leave it blank.
| GitHub Runner | Micro Docker Image | Medium Docker Image | Large Docker Image |
| --------------- | -------------------------------- | ------------------------------------------------- | -------------------------------------------------- |
| `ubuntu-latest` | [`node:16-buster-slim`][micro] | [`catthehacker/ubuntu:act-latest`][docker_images] | [`catthehacker/ubuntu:full-latest`][docker_images] |
| `ubuntu-22.04` | [`node:16-bullseye-slim`][micro] | [`catthehacker/ubuntu:act-22.04`][docker_images] | `unavailable` |
| `ubuntu-20.04` | [`node:16-buster-slim`][micro] | [`catthehacker/ubuntu:act-20.04`][docker_images] | [`catthehacker/ubuntu:full-20.04`][docker_images] |
| `ubuntu-18.04` | [`node:16-buster-slim`][micro] | [`catthehacker/ubuntu:act-18.04`][docker_images] | [`catthehacker/ubuntu:full-18.04`][docker_images] |
The process looks like:
[micro]: https://hub.docker.com/_/buildpack-deps
[docker_images]: https://github.com/catthehacker/docker_images
```text
INFO Registering runner, arch=amd64, os=darwin, version=0.1.5.
WARN Runner in user-mode.
INFO Enter the Gitea instance URL (for example, https://gitea.com/):
http://192.168.8.8:3000/
INFO Enter the runner token:
fe884e8027dc292970d4e0303fe82b14xxxxxxxx
INFO Enter the runner name (if set empty, use hostname: Test.local):
Windows and macOS based platforms are currently **unsupported and won't work** (see issue [#97](https://github.com/nektos/act/issues/97))
INFO Enter the runner labels, leave blank to use the default labels (comma-separated, for example, ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest):
## Please see [IMAGES.md](./IMAGES.md) for more information about the Docker images that can be used with `act`
## Default runners are intentionally incomplete
These default images do **not** contain **all** the tools that GitHub Actions offers by default in their runners.
Many things can work improperly or not at all while running those image.
Additionally, some software might still not work even if installed properly, since GitHub Actions are running in fully virtualized machines while `act` is using Docker containers (e.g. Docker does not support running `systemd`).
In case of any problems [please create issue](https://github.com/nektos/act/issues/new/choose) in respective repository (issues with `act` in this repository, issues with `nektos/act-environments-ubuntu:18.04` in [`nektos/act-environments`](https://github.com/nektos/act-environments) and issues with any image from user `catthehacker` in [`catthehacker/docker_images`](https://github.com/catthehacker/docker_images))
## Alternative runner images
If you need an environment that works just like the corresponding GitHub runner then consider using an image provided by [nektos/act-environments](https://github.com/nektos/act-environments):
- [`nektos/act-environments-ubuntu:18.04`](https://hub.docker.com/r/nektos/act-environments-ubuntu/tags) - built from the Packer file GitHub uses in [actions/virtual-environments](https://github.com/actions/runner).
:warning: :elephant: `*** WARNING - this image is >18GB 😱***`
- [`catthehacker/ubuntu:full-*`](https://github.com/catthehacker/docker_images/pkgs/container/ubuntu) - built from Packer template provided by GitHub, see [catthehacker/virtual-environments-fork](https://github.com/catthehacker/virtual-environments-fork) or [catthehacker/docker_images](https://github.com/catthehacker/docker_images) for more information
## Using local runner images
The `--pull` flag is set to true by default due to a breaking on older default docker images. This would pull the docker image everytime act is executed.
Set `--pull` to false if a local docker image is needed
```sh
act --pull=false
INFO Registering runner, name=Test.local, instance=http://192.168.8.8:3000/, labels=[ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04 ubuntu-20.04:docker://docker.gitea.com/runner-images:ubuntu-20.04].
DEBU Successfully pinged the Gitea instance server
INFO Runner registered successfully.
```
## Use an alternative runner image
You can also register with command line arguments.
To use a different image for the runner, use the `-P` option.
```sh
act -P <platform>=<docker-image>
```bash
./gitea-runner register --instance http://192.168.8.8:3000 --token <my_runner_token> --no-interactive
```
If your workflow uses `ubuntu-18.04`, consider below line as an example for changing Docker image used to run that workflow:
If the registry succeed, it will run immediately. Next time, you could run the runner directly.
```sh
act -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04
### Run
```bash
./gitea-runner daemon
```
If you use multiple platforms in your workflow, you have to specify them to change which image is used.
For example, if your workflow uses `ubuntu-18.04`, `ubuntu-16.04` and `ubuntu-latest`, specify all platforms like below
### Run with docker
```sh
act -P ubuntu-18.04=nektos/act-environments-ubuntu:18.04 -P ubuntu-latest=ubuntu:latest -P ubuntu-16.04=node:16-buster-slim
```bash
docker run -e GITEA_INSTANCE_URL=https://your_gitea.com -e GITEA_RUNNER_REGISTRATION_TOKEN=<your_token> -v /var/run/docker.sock:/var/run/docker.sock --name my_runner gitea/runner:nightly
```
# Secrets
Mount a volume on `/data` if you want the registration file and optional config to survive container recreation (see [scripts/run.sh](scripts/run.sh)).
To run `act` with secrets, you can enter them interactively, supply them as environment variables or load them from a file. The following options are available for providing secrets:
### Configuration
- `act -s MY_SECRET=somevalue` - use `somevalue` as the value for `MY_SECRET`.
- `act -s MY_SECRET` - check for an environment variable named `MY_SECRET` and use it if it exists. If the environment variable is not defined, prompt the user for a value.
- `act --secret-file my.secrets` - load secrets values from `my.secrets` file.
- secrets file format is the same as `.env` format
The runner is configured with a YAML file. Generate a starting point (this matches what ships in the tree):
# Configuration
You can provide default configuration flags to `act` by either creating a `./.actrc` or a `~/.actrc` file. Any flags in the files will be applied before any flags provided directly on the command line. For example, a file like below will always use the `nektos/act-environments-ubuntu:18.04` image for the `ubuntu-latest` runner:
```sh
# sample .actrc file
-P ubuntu-latest=nektos/act-environments-ubuntu:18.04
```bash
./gitea-runner generate-config > config.yaml
```
Additionally, act supports loading environment variables from an `.env` file. The default is to look in the working directory for the file but can be overridden by:
Pass it with `-c` / `--config` on any command that loads configuration (`register`, `daemon`, `cache-server`):
```sh
act --env-file my.env
```bash
./gitea-runner -c config.yaml register
./gitea-runner -c config.yaml daemon
./gitea-runner -c config.yaml cache-server
```
`.env`:
Every option is described in [config.example.yaml](internal/pkg/config/config.example.yaml) (the same content `generate-config` prints).
```env
MY_ENV_VAR=MY_ENV_VAR_VALUE
MY_2ND_ENV_VAR="my 2nd env var value"
```
#### Without a config file
# Skipping jobs
If you omit `-c`, built-in defaults apply (same as an empty YAML document). A small set of **deprecated** environment variables can still override parts of that default config, but **only when no `-c` path was given**; they are ignored if you use a config file:
You cannot use the `env` context in job level if conditions, but you can add a custom event property to the `github` context. You can use this method also on step level if conditions.
| Variable | Effect |
| --- | --- |
| `GITEA_DEBUG` | If true, sets log level to `debug` |
| `GITEA_TRACE` | If true, sets log level to `trace` |
| `GITEA_RUNNER_CAPACITY` | Concurrent jobs (integer) |
| `GITEA_RUNNER_FILE` | Registration state file path (default `.runner`) |
| `GITEA_RUNNER_ENVIRON` | Extra job env vars as comma-separated `KEY:VALUE` pairs |
| `GITEA_RUNNER_ENV_FILE` | Path to an env file merged into job env (same idea as `runner.env_file` in YAML) |
```yml
on: push
jobs:
deploy:
if: ${{ !github.event.act }} # skip during local actions testing
runs-on: ubuntu-latest
steps:
- run: exit 0
```
Prefer a YAML file for all settings.
And use this `event.json` file with act otherwise the Job will run:
#### Registration vs config labels
```json
{
"act": true
}
```
If `runner.labels` is set in the YAML file, those labels are used during `register` and the `--labels` CLI flag is ignored.
Run act like
#### External cache (`actions/cache`)
```sh
act -e event.json
```
If `cache.external_server` is set, you must set `cache.external_secret` to the same value on this runner and on the standalone cache server. Run the server with `gitea-runner cache-server` using a config that defines `cache.external_secret` (and matching `cache.dir` / host / port as needed). Flags `--dir`, `--host`, and `--port` on `cache-server` override the file.
_Hint: you can add / append `-e event.json` as a line into `./.actrc`_
#### Official Docker image
# Skipping steps
Besides `GITEA_INSTANCE_URL` and `GITEA_RUNNER_REGISTRATION_TOKEN`, the image entrypoint supports optional variables such as `CONFIG_FILE` (passed through as `-c`), `GITEA_RUNNER_LABELS`, `GITEA_RUNNER_EPHEMERAL`, `GITEA_RUNNER_ONCE`, `GITEA_RUNNER_NAME`, `GITEA_MAX_REG_ATTEMPTS`, `RUNNER_STATE_FILE`, and `GITEA_RUNNER_REGISTRATION_TOKEN_FILE`. See [scripts/run.sh](scripts/run.sh) for exact behavior.
Act adds a special environment variable `ACT` that can be used to skip a step that you
don't want to run locally. E.g. a step that posts a Slack message or bumps a version number.
**You cannot use this method in job level if conditions, see [Skipping jobs](#skipping-jobs)**
For a fuller container-oriented walkthrough, see [examples/docker](examples/docker/README.md).
```yml
- name: Some step
if: ${{ !env.ACT }}
run: |
...
```
When `container.bind_workdir` is enabled, stale task workspace directories can be cleaned while the runner is idle:
- directories older than `runner.workdir_cleanup_age` are removed (default: `24h`; set `0` to disable)
- cleanup runs every `runner.idle_cleanup_interval` (default: `10m`; set `0` to disable)
- only purely numeric subdirectories under `container.workdir_parent` are treated as task workspaces and may be removed
- cleanup assumes `container.workdir_parent` is not shared across multiple runners
# Events
### Example Deployments
Every [GitHub event](https://developer.github.com/v3/activity/events/types) is accompanied by a payload. You can provide these events in JSON format with the `--eventpath` to simulate specific GitHub events kicking off an action. For example:
```json
{
"pull_request": {
"head": {
"ref": "sample-head-ref"
},
"base": {
"ref": "sample-base-ref"
}
}
}
```
```sh
act pull_request -e pull-request.json
```
Act will properly provide `github.head_ref` and `github.base_ref` to the action as expected.
# Pass Inputs to Manually Triggered Workflows
Example workflow file
```yaml
on:
workflow_dispatch:
inputs:
NAME:
description: "A random input name for the workflow"
type: string
SOME_VALUE:
description: "Some other input to pass"
type: string
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Test with inputs
run: |
echo "Hello ${{ github.event.inputs.NAME }} and ${{ github.event.inputs.SOME_VALUE }}!"
```
## via input or input-file flag
- `act --input NAME=somevalue` - use `somevalue` as the value for `NAME` input.
- `act --input-file my.input` - load input values from `my.input` file.
- input file format is the same as `.env` format
## via JSON
Example JSON payload file conveniently named `payload.json`
```json
{
"inputs": {
"NAME": "Manual Workflow",
"SOME_VALUE": "ABC"
}
}
```
Command for triggering the workflow
```sh
act workflow_dispatch -e payload.json
```
# GitHub Enterprise
Act supports using and authenticating against private GitHub Enterprise servers.
To use your custom GHE server, set the CLI flag `--github-instance` to your hostname (e.g. `github.company.com`).
Please note that if your GHE server requires authentication, we will use the secret provided via `GITHUB_TOKEN`.
Please also see the [official documentation for GitHub actions on GHE](https://docs.github.com/en/enterprise-server@3.0/admin/github-actions/about-using-actions-in-your-enterprise) for more information on how to use actions.
# Support
Need help? Ask on [Gitter](https://gitter.im/nektos/act)!
# Contributing
Want to contribute to act? Awesome! Check out the [contributing guidelines](CONTRIBUTING.md) to get involved.
## Manually building from source
- Install Go tools 1.20+ - (<https://golang.org/doc/install>)
- Clone this repo `git clone git@github.com:nektos/act.git`
- Run unit tests with `make test`
- Build and install: `make install`
Check out the [examples](examples) directory for sample deployment types.

View File

@@ -1,5 +0,0 @@
VERIFICATION
Verification is intended to assist the Chocolatey moderators and community
in verifying that this package's contents are trustworthy.
Checksums: https://github.com/nektos/act/releases, in the checksums.txt file

View File

@@ -1 +0,0 @@
0.2.51

View File

@@ -1,26 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Do not remove this test for UTF-8: if “Ω” doesn’t appear as greek uppercase omega letter enclosed in quotation marks, you should use an editor that supports UTF-8, not this one. -->
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<id>act-cli</id>
<version>0.0.0</version>
<packageSourceUrl>https://github.com/nektos/act</packageSourceUrl>
<owners>nektos</owners>
<title>act (GitHub Actions CLI)</title>
<authors>nektos</authors>
<projectUrl>https://github.com/nektos/act</projectUrl>
<iconUrl>https://raw.githubusercontent.com/wiki/nektos/act/img/logo-150.png</iconUrl>
<copyright>Nektos</copyright>
<licenseUrl>https://raw.githubusercontent.com/nektos/act/master/LICENSE</licenseUrl>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<projectSourceUrl>https://github.com/nektos/act</projectSourceUrl>
<docsUrl>https://raw.githubusercontent.com/nektos/act/master/README.md</docsUrl>
<bugTrackerUrl>https://github.com/nektos/act/issues</bugTrackerUrl>
<tags>act github-actions actions golang ci devops</tags>
<summary>Run your GitHub Actions locally 🚀</summary>
<description>Run your GitHub Actions locally 🚀</description>
</metadata>
<files>
<file src="tools/**" target="tools" />
</files>
</package>

View File

@@ -1,6 +1,7 @@
The MIT License (MIT)
MIT License
Copyright (c) 2016 David Frank
Copyright (c) 2022 The Gitea Authors
Copyright (c) 2019
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -19,4 +20,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,3 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2023 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// Package artifactcache provides a cache handler for the runner.
//
// Inspired by https://github.com/sp-ricard-valverde/github-act-cache-server

View File

@@ -0,0 +1,879 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2023 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package artifactcache
import (
"context"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
"gitea.com/gitea/runner/act/common"
"github.com/julienschmidt/httprouter"
"github.com/sirupsen/logrus"
"github.com/timshannon/bolthold"
"go.etcd.io/bbolt"
)
const (
apiPath = "/_apis/artifactcache"
internalPath = "/_internal"
// artifactURLTTL bounds how long a signed artifactLocation URL stays valid.
// Short enough that a leaked URL is near-worthless; long enough to let the
// @actions/cache client download a big blob that was returned from /cache.
artifactURLTTL = 10 * time.Minute
)
type credKey struct{}
// JobCredential ties a per-job bearer token (ACTIONS_RUNTIME_TOKEN) to the
// repository that owns it. Every cache entry is stamped with Repo on
// reserve/commit and checked on read/write so one repo can never observe or
// poison another repo's cache, even from inside a container that reaches the
// cache server over the docker bridge network.
type JobCredential struct {
Repo string
}
// credEntry holds a registered job's credential along with an active
// registration count. RegisterJob is reference-counted so that if two tasks
// briefly share an ACTIONS_RUNTIME_TOKEN — e.g. a runner that retries a task
// after a crash before the old registration is revoked — the first task's
// revoker does not cut the second task's auth out from under it.
type credEntry struct {
cred JobCredential
refs int
}
type Handler struct {
dir string
storage *Storage
router *httprouter.Router
listener net.Listener
server *http.Server
logger logrus.FieldLogger
gcing atomic.Bool
gcAt time.Time
outboundIP string
// internalSecret guards /_internal/{register,revoke}. When set, a remote
// runner can use these endpoints to pre-register per-job
// ACTIONS_RUNTIME_TOKENs against this server, enabling the same
// per-job auth and repo scoping as the embedded handler over the
// network. Empty disables the control-plane entirely.
internalSecret string
// secret signs short-lived artifact download URLs. The @actions/cache
// toolkit does not send Authorization on the download request, so blob
// GETs authenticate via a per-URL HMAC signature with expiry rather than
// via the bearer token used for management endpoints.
secret []byte
credMu sync.RWMutex
creds map[string]*credEntry
}
// StartHandler opens the on-disk cache store and starts the HTTP server.
//
// internalSecret, when non-empty, enables a control-plane API at
// /_internal/{register,revoke} that lets a remote runner pre-register the
// per-job ACTIONS_RUNTIME_TOKENs it expects this server to honor. The
// embedded in-process handler leaves it empty and registers tokens via the
// in-process RegisterJob method directly.
func StartHandler(dir, outboundIP string, port uint16, internalSecret string, logger logrus.FieldLogger) (*Handler, error) {
h := &Handler{
creds: make(map[string]*credEntry),
internalSecret: internalSecret,
}
if logger == nil {
discard := logrus.New()
discard.Out = io.Discard
logger = discard
}
logger = logger.WithField("module", "artifactcache")
h.logger = logger
if dir == "" {
home, err := os.UserHomeDir()
if err != nil {
return nil, err
}
dir = filepath.Join(home, ".cache", "actcache")
}
if err := os.MkdirAll(dir, 0o755); err != nil {
return nil, err
}
h.dir = dir
storage, err := NewStorage(filepath.Join(dir, "cache"))
if err != nil {
return nil, err
}
h.storage = storage
if outboundIP != "" {
h.outboundIP = outboundIP
} else if ip := common.GetOutboundIP(); ip == nil {
return nil, errors.New("unable to determine outbound IP address")
} else {
h.outboundIP = ip.String()
}
secret, err := loadOrCreateSecret(dir)
if err != nil {
return nil, err
}
h.secret = secret
router := httprouter.New()
router.GET(apiPath+"/cache", h.bearerAuth(h.find))
router.POST(apiPath+"/caches", h.bearerAuth(h.reserve))
router.PATCH(apiPath+"/caches/:id", h.bearerAuth(h.upload))
router.POST(apiPath+"/caches/:id", h.bearerAuth(h.commit))
router.POST(apiPath+"/clean", h.bearerAuth(h.clean))
// Artifact GET is signed via query-string HMAC because @actions/cache
// does not attach Authorization when downloading archiveLocation.
router.GET(apiPath+"/artifacts/:id", h.signedURLAuth(h.get))
// Control-plane: a remote runner registers/revokes per-job tokens so the
// cache API can authenticate them. Always wired so the routes exist; the
// handlers themselves 401 when internalSecret is unset.
router.POST(internalPath+"/register", h.internalAuth(h.internalRegister))
router.POST(internalPath+"/revoke", h.internalAuth(h.internalRevoke))
h.router = router
h.gcCache()
// Listen on all interfaces. Binding to outboundIP only would give no real
// security benefit (it is the LAN/internet-facing address either way) and
// can break Docker Desktop variants where the host's outbound IP is not
// routable from inside the container network. Authentication is enforced
// by the bearer middleware and per-repo scoping, not by reachability.
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
if err != nil {
return nil, err
}
server := &http.Server{
ReadHeaderTimeout: 2 * time.Second,
Handler: router,
}
go func() {
if err := server.Serve(listener); err != nil && errors.Is(err, net.ErrClosed) {
logger.Errorf("http serve: %v", err)
}
}()
h.listener = listener
h.server = server
return h, nil
}
func (h *Handler) ExternalURL() string {
// TODO: make the external url configurable if necessary
return fmt.Sprintf("http://%s:%d",
h.outboundIP,
h.listener.Addr().(*net.TCPAddr).Port)
}
// RegisterJob makes token a valid bearer credential for cache requests from
// the given repository and returns a function that removes it. The runner
// calls this at job start and defers the returned func so that the credential
// is only accepted while the job is running.
//
// Registrations are reference-counted: if a token is already registered, the
// existing repo is kept and the refcount is incremented. The entry is
// removed only when every revoker returned by RegisterJob has been called.
// This keeps a stray re-registration from silently revoking a live job.
func (h *Handler) RegisterJob(token, repo string) func() {
if h == nil || token == "" {
return func() {}
}
h.credMu.Lock()
if existing, ok := h.creds[token]; ok {
existing.refs++
} else {
h.creds[token] = &credEntry{
cred: JobCredential{Repo: repo},
refs: 1,
}
}
h.credMu.Unlock()
return func() {
h.credMu.Lock()
if entry, ok := h.creds[token]; ok {
entry.refs--
if entry.refs <= 0 {
delete(h.creds, token)
}
}
h.credMu.Unlock()
}
}
// RevokeJob explicitly revokes one registration of token, mirroring one call
// of the closure returned by RegisterJob. Used by the control-plane endpoint
// so a remote runner can revoke without holding the closure.
func (h *Handler) RevokeJob(token string) {
if h == nil || token == "" {
return
}
h.credMu.Lock()
if entry, ok := h.creds[token]; ok {
entry.refs--
if entry.refs <= 0 {
delete(h.creds, token)
}
}
h.credMu.Unlock()
}
func (h *Handler) lookupCredential(token string) (JobCredential, bool) {
h.credMu.RLock()
entry, ok := h.creds[token]
h.credMu.RUnlock()
if !ok {
return JobCredential{}, false
}
return entry.cred, true
}
// loadOrCreateSecret returns the 32-byte HMAC signing key for artifact URLs,
// persisted in dir/.secret so signed URLs handed out before a restart stay
// valid across the restart and so the standalone cache-server can be pointed
// at by config.Cache.ExternalServer without the URL rotating.
func loadOrCreateSecret(dir string) ([]byte, error) {
path := filepath.Join(dir, ".secret")
if data, err := os.ReadFile(path); err == nil {
if secret, err := hex.DecodeString(strings.TrimSpace(string(data))); err == nil && len(secret) >= 32 {
return secret, nil
}
} else if !os.IsNotExist(err) {
return nil, fmt.Errorf("read cache secret: %w", err)
}
secret := make([]byte, 32)
if _, err := rand.Read(secret); err != nil {
return nil, fmt.Errorf("generate cache secret: %w", err)
}
if err := os.WriteFile(path, []byte(hex.EncodeToString(secret)), 0o600); err != nil {
return nil, fmt.Errorf("write cache secret: %w", err)
}
return secret, nil
}
func (h *Handler) Close() error {
if h == nil {
return nil
}
var retErr error
if h.server != nil {
err := h.server.Close()
if err != nil {
retErr = err
}
h.server = nil
}
if h.listener != nil {
err := h.listener.Close()
if errors.Is(err, net.ErrClosed) {
err = nil
}
if err != nil {
retErr = err
}
h.listener = nil
}
return retErr
}
func (h *Handler) openDB() (*bolthold.Store, error) {
return bolthold.Open(filepath.Join(h.dir, "bolt.db"), 0o644, &bolthold.Options{
Encoder: json.Marshal,
Decoder: json.Unmarshal,
Options: &bbolt.Options{
Timeout: 5 * time.Second,
NoGrowSync: bbolt.DefaultOptions.NoGrowSync,
FreelistType: bbolt.DefaultOptions.FreelistType,
},
})
}
// GET /_apis/artifactcache/cache
func (h *Handler) find(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
cred := credFromContext(r.Context())
keys := strings.Split(r.URL.Query().Get("keys"), ",")
version := r.URL.Query().Get("version")
db, err := h.openDB()
if err != nil {
h.responseJSON(w, r, 500, err)
return
}
defer db.Close()
cache, err := findCache(db, cred.Repo, keys, version)
if err != nil {
h.responseJSON(w, r, 500, err)
return
}
if cache == nil {
h.responseJSON(w, r, 204)
return
}
if ok, err := h.storage.Exist(cache.ID); err != nil {
h.responseJSON(w, r, 500, err)
return
} else if !ok {
_ = db.Delete(cache.ID, cache)
h.responseJSON(w, r, 204)
return
}
h.responseJSON(w, r, 200, map[string]any{
"result": "hit",
"archiveLocation": h.signedArtifactURL(cache.ID, time.Now().Add(artifactURLTTL)),
"cacheKey": cache.Key,
})
}
// POST /_apis/artifactcache/caches
func (h *Handler) reserve(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
cred := credFromContext(r.Context())
api := &Request{}
if err := json.NewDecoder(r.Body).Decode(api); err != nil {
h.responseJSON(w, r, 400, err)
return
}
cache := api.ToCache()
cache.Repo = cred.Repo
db, err := h.openDB()
if err != nil {
h.responseJSON(w, r, 500, err)
return
}
defer db.Close()
now := time.Now().Unix()
cache.CreatedAt = now
cache.UsedAt = now
if err := insertCache(db, cache); err != nil {
h.responseJSON(w, r, 500, err)
return
}
h.responseJSON(w, r, 200, map[string]any{
"cacheId": cache.ID,
})
}
// PATCH /_apis/artifactcache/caches/:id
func (h *Handler) upload(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
cred := credFromContext(r.Context())
id, err := strconv.ParseInt(params.ByName("id"), 10, 64)
if err != nil {
h.responseJSON(w, r, 400, err)
return
}
cache := &Cache{}
db, err := h.openDB()
if err != nil {
h.responseJSON(w, r, 500, err)
return
}
defer db.Close()
if err := db.Get(id, cache); err != nil {
if errors.Is(err, bolthold.ErrNotFound) {
h.responseJSON(w, r, 400, fmt.Errorf("cache %d: not reserved", id))
return
}
h.responseJSON(w, r, 500, err)
return
}
if cache.Repo != cred.Repo {
h.responseJSON(w, r, 403, fmt.Errorf("cache %d: forbidden", id))
return
}
if cache.Complete {
h.responseJSON(w, r, 400, fmt.Errorf("cache %v %q: already complete", cache.ID, cache.Key))
return
}
db.Close()
start, _, err := parseContentRange(r.Header.Get("Content-Range"))
if err != nil {
h.responseJSON(w, r, 400, err)
return
}
if err := h.storage.Write(cache.ID, start, r.Body); err != nil {
h.responseJSON(w, r, 500, err)
return
}
h.useCache(id)
h.responseJSON(w, r, 200)
}
// POST /_apis/artifactcache/caches/:id
func (h *Handler) commit(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
cred := credFromContext(r.Context())
id, err := strconv.ParseInt(params.ByName("id"), 10, 64)
if err != nil {
h.responseJSON(w, r, 400, err)
return
}
cache := &Cache{}
db, err := h.openDB()
if err != nil {
h.responseJSON(w, r, 500, err)
return
}
defer db.Close()
if err := db.Get(id, cache); err != nil {
if errors.Is(err, bolthold.ErrNotFound) {
h.responseJSON(w, r, 400, fmt.Errorf("cache %d: not reserved", id))
return
}
h.responseJSON(w, r, 500, err)
return
}
if cache.Repo != cred.Repo {
h.responseJSON(w, r, 403, fmt.Errorf("cache %d: forbidden", id))
return
}
if cache.Complete {
h.responseJSON(w, r, 400, fmt.Errorf("cache %v %q: already complete", cache.ID, cache.Key))
return
}
db.Close()
size, err := h.storage.Commit(cache.ID, cache.Size)
if err != nil {
h.responseJSON(w, r, 500, err)
return
}
// write real size back to cache, it may be different from the current value when the request doesn't specify it.
cache.Size = size
db, err = h.openDB()
if err != nil {
h.responseJSON(w, r, 500, err)
return
}
defer db.Close()
cache.Complete = true
if err := db.Update(cache.ID, cache); err != nil {
h.responseJSON(w, r, 500, err)
return
}
h.responseJSON(w, r, 200)
}
// GET /_apis/artifactcache/artifacts/:id
// Authenticated via signed URL (see signedURLAuth), not bearer, because the
// @actions/cache toolkit downloads archiveLocation without Authorization.
// Repository scoping is already enforced at find() time; the signature binds
// the URL to the specific cache ID and an expiry.
func (h *Handler) get(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
id, err := strconv.ParseInt(params.ByName("id"), 10, 64)
if err != nil {
h.responseJSON(w, r, 400, err)
return
}
h.useCache(id)
h.storage.Serve(w, r, uint64(id))
}
// POST /_apis/artifactcache/clean
func (h *Handler) clean(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
// TODO: don't support force deleting cache entries
// see: https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#force-deleting-cache-entries
h.responseJSON(w, r, 200)
}
// bearerAuth resolves ACTIONS_RUNTIME_TOKEN against the set of currently
// registered jobs. A match attaches the job's JobCredential to the request
// context; a miss returns 401 before the handler body runs.
func (h *Handler) bearerAuth(handler httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
h.logger.Debugf("%s %s", r.Method, r.URL.Path)
token := bearerToken(r)
if token == "" {
h.responseJSON(w, r, http.StatusUnauthorized, errors.New("missing bearer token"))
return
}
cred, ok := h.lookupCredential(token)
if !ok {
h.responseJSON(w, r, http.StatusUnauthorized, errors.New("unknown bearer token"))
return
}
ctx := context.WithValue(r.Context(), credKey{}, cred)
handler(w, r.WithContext(ctx), params)
go h.gcCache()
}
}
func (h *Handler) signedURLAuth(handler httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
h.logger.Debugf("%s %s", r.Method, r.URL.Path)
id, err := strconv.ParseInt(params.ByName("id"), 10, 64)
if err != nil {
h.responseJSON(w, r, 400, err)
return
}
expStr := r.URL.Query().Get("exp")
sig := r.URL.Query().Get("sig")
if expStr == "" || sig == "" {
h.responseJSON(w, r, http.StatusUnauthorized, errors.New("missing signature"))
return
}
exp, err := strconv.ParseInt(expStr, 10, 64)
if err != nil {
h.responseJSON(w, r, http.StatusUnauthorized, errors.New("invalid expiry"))
return
}
if time.Now().Unix() > exp {
h.responseJSON(w, r, http.StatusUnauthorized, errors.New("signature expired"))
return
}
expected := h.computeSignature(id, exp)
if !hmac.Equal([]byte(sig), []byte(expected)) {
h.responseJSON(w, r, http.StatusUnauthorized, errors.New("bad signature"))
return
}
handler(w, r, params)
go h.gcCache()
}
}
// internalAuth gates the control-plane endpoints. The bearer must
// constant-time-equal the configured internalSecret. If the secret is empty,
// the control-plane is disabled and every request gets 404 — which matches
// the upstream nektos/act behavior of "the route does not exist".
func (h *Handler) internalAuth(handler httprouter.Handle) httprouter.Handle {
return func(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
if h.internalSecret == "" {
http.NotFound(w, r)
return
}
token := bearerToken(r)
if token == "" || !hmac.Equal([]byte(token), []byte(h.internalSecret)) {
h.responseJSON(w, r, http.StatusUnauthorized, errors.New("internal: bad secret"))
return
}
handler(w, r, params)
}
}
type internalRegisterBody struct {
Token string `json:"token"`
Repo string `json:"repo"`
}
type internalRevokeBody struct {
Token string `json:"token"`
}
// POST /_internal/register
func (h *Handler) internalRegister(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
var body internalRegisterBody
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
h.responseJSON(w, r, http.StatusBadRequest, err)
return
}
if body.Token == "" {
h.responseJSON(w, r, http.StatusBadRequest, errors.New("token is required"))
return
}
h.RegisterJob(body.Token, body.Repo)
h.responseJSON(w, r, http.StatusOK)
}
// POST /_internal/revoke
func (h *Handler) internalRevoke(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
var body internalRevokeBody
if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
h.responseJSON(w, r, http.StatusBadRequest, err)
return
}
if body.Token == "" {
h.responseJSON(w, r, http.StatusBadRequest, errors.New("token is required"))
return
}
h.RevokeJob(body.Token)
h.responseJSON(w, r, http.StatusOK)
}
func bearerToken(r *http.Request) string {
auth := r.Header.Get("Authorization")
const prefix = "Bearer "
if len(auth) > len(prefix) && strings.EqualFold(auth[:len(prefix)], prefix) {
return auth[len(prefix):]
}
return ""
}
func credFromContext(ctx context.Context) JobCredential {
if cred, ok := ctx.Value(credKey{}).(JobCredential); ok {
return cred
}
return JobCredential{}
}
func (h *Handler) computeSignature(cacheID, exp int64) string {
mac := hmac.New(sha256.New, h.secret)
fmt.Fprintf(mac, "%d:%d", cacheID, exp)
return hex.EncodeToString(mac.Sum(nil))
}
func (h *Handler) signedArtifactURL(cacheID uint64, exp time.Time) string {
expUnix := exp.Unix()
sig := h.computeSignature(int64(cacheID), expUnix)
q := url.Values{}
q.Set("exp", strconv.FormatInt(expUnix, 10))
q.Set("sig", sig)
return fmt.Sprintf("%s%s/artifacts/%d?%s", h.ExternalURL(), apiPath, cacheID, q.Encode())
}
// if not found, return (nil, nil) instead of an error.
func findCache(db *bolthold.Store, repo string, keys []string, version string) (*Cache, error) {
cache := &Cache{}
for _, prefix := range keys {
// if a key in the list matches exactly, don't return partial matches
if err := db.FindOne(cache,
bolthold.Where("Repo").Eq(repo).
And("Key").Eq(prefix).
And("Version").Eq(version).
And("Complete").Eq(true).
SortBy("CreatedAt").Reverse()); err == nil || !errors.Is(err, bolthold.ErrNotFound) {
if err != nil {
return nil, fmt.Errorf("find cache: %w", err)
}
return cache, nil
}
prefixPattern := "^" + regexp.QuoteMeta(prefix)
re, err := regexp.Compile(prefixPattern)
if err != nil {
continue
}
if err := db.FindOne(cache,
bolthold.Where("Repo").Eq(repo).
And("Key").RegExp(re).
And("Version").Eq(version).
And("Complete").Eq(true).
SortBy("CreatedAt").Reverse()); err != nil {
if errors.Is(err, bolthold.ErrNotFound) {
continue
}
return nil, fmt.Errorf("find cache: %w", err)
}
return cache, nil
}
return nil, nil //nolint:nilnil // pre-existing issue from nektos/act
}
func insertCache(db *bolthold.Store, cache *Cache) error {
if err := db.Insert(bolthold.NextSequence(), cache); err != nil {
return fmt.Errorf("insert cache: %w", err)
}
// write back id to db
if err := db.Update(cache.ID, cache); err != nil {
return fmt.Errorf("write back id to db: %w", err)
}
return nil
}
func (h *Handler) useCache(id int64) {
db, err := h.openDB()
if err != nil {
return
}
defer db.Close()
cache := &Cache{}
if err := db.Get(id, cache); err != nil {
return
}
cache.UsedAt = time.Now().Unix()
_ = db.Update(cache.ID, cache)
}
const (
keepUsed = 30 * 24 * time.Hour
keepUnused = 7 * 24 * time.Hour
keepTemp = 5 * time.Minute
keepOld = 5 * time.Minute
)
func (h *Handler) gcCache() {
if h.gcing.Load() {
return
}
if !h.gcing.CompareAndSwap(false, true) {
return
}
defer h.gcing.Store(false)
if time.Since(h.gcAt) < time.Hour {
h.logger.Debugf("skip gc: %v", h.gcAt.String())
return
}
h.gcAt = time.Now()
h.logger.Debugf("gc: %v", h.gcAt.String())
db, err := h.openDB()
if err != nil {
return
}
defer db.Close()
// Remove the caches which are not completed for a while, they are most likely to be broken.
var caches []*Cache
if err := db.Find(&caches, bolthold.
Where("UsedAt").Lt(time.Now().Add(-keepTemp).Unix()).
And("Complete").Eq(false),
); err != nil {
h.logger.Warnf("find caches: %v", err)
} else {
for _, cache := range caches {
h.storage.Remove(cache.ID)
if err := db.Delete(cache.ID, cache); err != nil {
h.logger.Warnf("delete cache: %v", err)
continue
}
h.logger.Infof("deleted cache: %+v", cache)
}
}
// Remove the old caches which have not been used recently.
caches = caches[:0]
if err := db.Find(&caches, bolthold.
Where("UsedAt").Lt(time.Now().Add(-keepUnused).Unix()),
); err != nil {
h.logger.Warnf("find caches: %v", err)
} else {
for _, cache := range caches {
h.storage.Remove(cache.ID)
if err := db.Delete(cache.ID, cache); err != nil {
h.logger.Warnf("delete cache: %v", err)
continue
}
h.logger.Infof("deleted cache: %+v", cache)
}
}
// Remove the old caches which are too old.
caches = caches[:0]
if err := db.Find(&caches, bolthold.
Where("CreatedAt").Lt(time.Now().Add(-keepUsed).Unix()),
); err != nil {
h.logger.Warnf("find caches: %v", err)
} else {
for _, cache := range caches {
h.storage.Remove(cache.ID)
if err := db.Delete(cache.ID, cache); err != nil {
h.logger.Warnf("delete cache: %v", err)
continue
}
h.logger.Infof("deleted cache: %+v", cache)
}
}
// Remove the old caches with the same key and version within the same
// repository, keep the latest one. Aggregation must include Repo so two
// repos that happen to share a (key, version) do not evict each other —
// otherwise per-repo scoping holds for reads but one repo can age
// another out after keepOld.
// Also keep the olds which have been used recently for a while in case of the cache is still in use.
if results, err := db.FindAggregate(
&Cache{},
bolthold.Where("Complete").Eq(true),
"Repo", "Key", "Version",
); err != nil {
h.logger.Warnf("find aggregate caches: %v", err)
} else {
for _, result := range results {
if result.Count() <= 1 {
continue
}
result.Sort("CreatedAt")
caches = caches[:0]
result.Reduction(&caches)
for _, cache := range caches[:len(caches)-1] {
if time.Since(time.Unix(cache.UsedAt, 0)) < keepOld {
// Keep it since it has been used recently, even if it's old.
// Or it could break downloading in process.
continue
}
h.storage.Remove(cache.ID)
if err := db.Delete(cache.ID, cache); err != nil {
h.logger.Warnf("delete cache: %v", err)
continue
}
h.logger.Infof("deleted cache: %+v", cache)
}
}
}
}
func (h *Handler) responseJSON(w http.ResponseWriter, r *http.Request, code int, v ...any) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
var data []byte
if len(v) == 0 || v[0] == nil {
data, _ = json.Marshal(struct{}{})
} else if err, ok := v[0].(error); ok {
h.logger.Errorf("%v %v: %v", r.Method, r.URL.Path, err)
data, _ = json.Marshal(map[string]any{
"error": err.Error(),
})
} else {
data, _ = json.Marshal(v[0])
}
w.WriteHeader(code)
_, _ = w.Write(data)
}
func parseContentRange(s string) (int64, int64, error) {
// support the format like "bytes 11-22/*" only
s, _, _ = strings.Cut(strings.TrimPrefix(s, "bytes "), "/")
s1, s2, _ := strings.Cut(s, "-")
start, err := strconv.ParseInt(s1, 10, 64)
if err != nil {
return 0, 0, fmt.Errorf("parse %q: %w", s, err)
}
stop, err := strconv.ParseInt(s2, 10, 64)
if err != nil {
return 0, 0, fmt.Errorf("parse %q: %w", s, err)
}
return start, stop, nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2023 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package artifactcache
type Request struct {
Key string `json:"key" `
Version string `json:"version"`
Size int64 `json:"cacheSize"`
}
func (c *Request) ToCache() *Cache {
if c == nil {
return nil
}
ret := &Cache{
Key: c.Key,
Version: c.Version,
Size: c.Size,
}
if c.Size == 0 {
// So the request comes from old versions of actions, like `actions/cache@v2`.
// It doesn't send cache size. Set it to -1 to indicate that.
ret.Size = -1
}
return ret
}
type Cache struct {
ID uint64 `json:"id" boltholdKey:"ID"`
Repo string `json:"repo" boltholdIndex:"Repo"`
Key string `json:"key" boltholdIndex:"Key"`
Version string `json:"version" boltholdIndex:"Version"`
Size int64 `json:"cacheSize"`
Complete bool `json:"complete" boltholdIndex:"Complete"`
UsedAt int64 `json:"usedAt" boltholdIndex:"UsedAt"`
CreatedAt int64 `json:"createdAt" boltholdIndex:"CreatedAt"`
}

View File

@@ -1,3 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2023 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package artifactcache
import (
@@ -6,6 +10,7 @@ import (
"net/http"
"os"
"path/filepath"
"strconv"
)
type Storage struct {
@@ -103,11 +108,11 @@ func (s *Storage) Remove(id uint64) {
}
func (s *Storage) filename(id uint64) string {
return filepath.Join(s.rootDir, fmt.Sprintf("%02x", id%0xff), fmt.Sprint(id))
return filepath.Join(s.rootDir, fmt.Sprintf("%02x", id%0xff), strconv.FormatUint(id, 10))
}
func (s *Storage) tempDir(id uint64) string {
return filepath.Join(s.rootDir, "tmp", fmt.Sprint(id))
return filepath.Join(s.rootDir, "tmp", strconv.FormatUint(id, 10))
}
func (s *Storage) tempName(id uint64, offset int64) string {

View File

@@ -1,3 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2021 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package artifacts
import (
@@ -13,9 +17,9 @@ import (
"strings"
"time"
"github.com/julienschmidt/httprouter"
"gitea.com/gitea/runner/act/common"
"github.com/nektos/act/pkg/common"
"github.com/julienschmidt/httprouter"
)
type FileContainerResourceURL struct {
@@ -55,8 +59,7 @@ type WriteFS interface {
OpenAppendable(name string) (WritableFile, error)
}
type readWriteFSImpl struct {
}
type readWriteFSImpl struct{}
func (fwfs readWriteFSImpl) Open(name string) (fs.File, error) {
return os.Open(name)
@@ -74,7 +77,6 @@ func (fwfs readWriteFSImpl) OpenAppendable(name string) (WritableFile, error) {
return nil, err
}
file, err := os.OpenFile(name, os.O_CREATE|os.O_RDWR, 0o644)
if err != nil {
return nil, err
}
@@ -88,7 +90,7 @@ func (fwfs readWriteFSImpl) OpenAppendable(name string) (WritableFile, error) {
var gzipExtension = ".gz__"
func safeResolve(baseDir string, relPath string) string {
func safeResolve(baseDir, relPath string) string {
return filepath.Join(baseDir, filepath.Clean(filepath.Join(string(os.PathSeparator), relPath)))
}
@@ -127,7 +129,6 @@ func uploads(router *httprouter.Router, baseDir string, fsys WriteFS) {
}
return fsys.OpenWritable(safePath)
}()
if err != nil {
panic(err)
}
@@ -275,7 +276,7 @@ func downloads(router *httprouter.Router, baseDir string, fsys fs.FS) {
})
}
func Serve(ctx context.Context, artifactPath string, addr string, port string) context.CancelFunc {
func Serve(ctx context.Context, artifactPath, addr, port string) context.CancelFunc {
serverContext, cancel := context.WithCancel(ctx)
logger := common.Logger(serverContext)

View File

@@ -0,0 +1,444 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2021 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package artifacts
import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"maps"
"net/http"
"net/http/httptest"
"net/url"
"os"
"path/filepath"
"strings"
"testing"
"testing/fstest"
"time"
"github.com/julienschmidt/httprouter"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
type writableMapFile struct {
fstest.MapFile
}
func (f *writableMapFile) Write(data []byte) (int, error) {
f.Data = data
return len(data), nil
}
func (f *writableMapFile) Close() error {
return nil
}
type writeMapFS struct {
fstest.MapFS
}
func (fsys writeMapFS) OpenWritable(name string) (WritableFile, error) {
file := &writableMapFile{
MapFile: fstest.MapFile{
Data: []byte("content2"),
},
}
fsys.MapFS[name] = &file.MapFile
return file, nil
}
func (fsys writeMapFS) OpenAppendable(name string) (WritableFile, error) {
file := &writableMapFile{
MapFile: fstest.MapFile{
Data: []byte("content2"),
},
}
fsys.MapFS[name] = &file.MapFile
return file, nil
}
func TestNewArtifactUploadPrepare(t *testing.T) {
assert := assert.New(t)
memfs := fstest.MapFS(map[string]*fstest.MapFile{})
router := httprouter.New()
uploads(router, "artifact/server/path", writeMapFS{memfs})
req, _ := http.NewRequest(http.MethodPost, "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
assert.Fail("Wrong status")
}
response := FileContainerResourceURL{}
err := json.Unmarshal(rr.Body.Bytes(), &response)
if err != nil {
panic(err)
}
assert.Equal("http://localhost/upload/1", response.FileContainerResourceURL)
}
func TestArtifactUploadBlob(t *testing.T) {
assert := assert.New(t)
memfs := fstest.MapFS(map[string]*fstest.MapFile{})
router := httprouter.New()
uploads(router, "artifact/server/path", writeMapFS{memfs})
req, _ := http.NewRequest(http.MethodPut, "http://localhost/upload/1?itemPath=some/file", strings.NewReader("content"))
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
assert.Fail("Wrong status")
}
response := ResponseMessage{}
err := json.Unmarshal(rr.Body.Bytes(), &response)
if err != nil {
panic(err)
}
assert.Equal("success", response.Message)
assert.Equal("content", string(memfs["artifact/server/path/1/some/file"].Data))
}
func TestFinalizeArtifactUpload(t *testing.T) {
assert := assert.New(t)
memfs := fstest.MapFS(map[string]*fstest.MapFile{})
router := httprouter.New()
uploads(router, "artifact/server/path", writeMapFS{memfs})
req, _ := http.NewRequest(http.MethodPatch, "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
assert.Fail("Wrong status")
}
response := ResponseMessage{}
err := json.Unmarshal(rr.Body.Bytes(), &response)
if err != nil {
panic(err)
}
assert.Equal("success", response.Message)
}
func TestListArtifacts(t *testing.T) {
assert := assert.New(t)
memfs := fstest.MapFS(map[string]*fstest.MapFile{
"artifact/server/path/1/file.txt": {
Data: []byte(""),
},
})
router := httprouter.New()
downloads(router, "artifact/server/path", memfs)
req, _ := http.NewRequest(http.MethodGet, "http://localhost/_apis/pipelines/workflows/1/artifacts", nil)
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
assert.FailNow(fmt.Sprintf("Wrong status: %d", status))
}
response := NamedFileContainerResourceURLResponse{}
err := json.Unmarshal(rr.Body.Bytes(), &response)
if err != nil {
panic(err)
}
assert.Equal(1, response.Count)
assert.Equal("file.txt", response.Value[0].Name)
assert.Equal("http://localhost/download/1", response.Value[0].FileContainerResourceURL)
}
func TestListArtifactContainer(t *testing.T) {
assert := assert.New(t)
memfs := fstest.MapFS(map[string]*fstest.MapFile{
"artifact/server/path/1/some/file": {
Data: []byte(""),
},
})
router := httprouter.New()
downloads(router, "artifact/server/path", memfs)
req, _ := http.NewRequest(http.MethodGet, "http://localhost/download/1?itemPath=some/file", nil)
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
assert.FailNow(fmt.Sprintf("Wrong status: %d", status))
}
response := ContainerItemResponse{}
err := json.Unmarshal(rr.Body.Bytes(), &response)
if err != nil {
panic(err)
}
assert.Len(response.Value, 1)
assert.Equal("some/file", response.Value[0].Path)
assert.Equal("file", response.Value[0].ItemType)
assert.Equal("http://localhost/artifact/1/some/file/.", response.Value[0].ContentLocation)
}
func TestDownloadArtifactFile(t *testing.T) {
assert := assert.New(t)
memfs := fstest.MapFS(map[string]*fstest.MapFile{
"artifact/server/path/1/some/file": {
Data: []byte("content"),
},
})
router := httprouter.New()
downloads(router, "artifact/server/path", memfs)
req, _ := http.NewRequest(http.MethodGet, "http://localhost/artifact/1/some/file", nil)
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
assert.FailNow(fmt.Sprintf("Wrong status: %d", status))
}
data := rr.Body.Bytes()
assert.Equal("content", string(data))
}
// TestArtifactFlow drives the real Serve() artifact server over a loopback socket, exercising
// the same upload -> finalize -> list -> download protocol the upload-artifact/download-artifact
// actions speak. Running it in-process (rather than from a job container) keeps it network-free
// and reachable everywhere, including when the CI job is itself a container.
func TestArtifactFlow(t *testing.T) {
artifactPath := t.TempDir()
// Serve the exact routes Serve() wires up, on a real loopback socket via httptest. httptest
// picks a free port and Close() tears the server down synchronously — avoiding both the
// port-rebind race and Serve()'s detached ListenAndServe goroutine, which logger.Fatal()s
// (process exit) on a bind error and can outlive the test's temp-dir cleanup.
router := httprouter.New()
fsys := readWriteFSImpl{}
uploads(router, artifactPath, fsys)
downloads(router, artifactPath, fsys)
server := httptest.NewServer(router)
defer server.Close()
baseURL := server.URL
client := server.Client()
client.Timeout = 5 * time.Second
// request performs one HTTP call and returns the status and body. The default transport adds
// Accept-Encoding: gzip and transparently decompresses, so gzipped downloads come back plain.
request := func(t *testing.T, method, rawURL string, body io.Reader, header http.Header) (int, []byte) {
t.Helper()
req, err := http.NewRequest(method, rawURL, body)
require.NoError(t, err)
maps.Copy(req.Header, header)
resp, err := client.Do(req)
require.NoError(t, err)
defer resp.Body.Close()
data, err := io.ReadAll(resp.Body)
require.NoError(t, err)
return resp.StatusCode, data
}
t.Run("upload-and-download", func(t *testing.T) {
const runID, item, content = "1", "my-artifact/data.txt", "hello artifact\n"
status, data := request(t, http.MethodPost, baseURL+"/_apis/pipelines/workflows/"+runID+"/artifacts", nil, nil)
require.Equal(t, http.StatusOK, status, string(data))
var prep FileContainerResourceURL
require.NoError(t, json.Unmarshal(data, &prep))
require.Equal(t, baseURL+"/upload/"+runID, prep.FileContainerResourceURL)
status, data = request(t, http.MethodPut, prep.FileContainerResourceURL+"?itemPath="+url.QueryEscape(item), strings.NewReader(content), nil)
require.Equal(t, http.StatusOK, status, string(data))
var msg ResponseMessage
require.NoError(t, json.Unmarshal(data, &msg))
require.Equal(t, "success", msg.Message)
status, data = request(t, http.MethodPatch, baseURL+"/_apis/pipelines/workflows/"+runID+"/artifacts", nil, nil)
require.Equal(t, http.StatusOK, status, string(data))
status, data = request(t, http.MethodGet, baseURL+"/_apis/pipelines/workflows/"+runID+"/artifacts", nil, nil)
require.Equal(t, http.StatusOK, status, string(data))
var list NamedFileContainerResourceURLResponse
require.NoError(t, json.Unmarshal(data, &list))
require.Equal(t, 1, list.Count)
require.Equal(t, "my-artifact", list.Value[0].Name)
status, data = request(t, http.MethodGet, list.Value[0].FileContainerResourceURL+"?itemPath=my-artifact", nil, nil)
require.Equal(t, http.StatusOK, status, string(data))
var items ContainerItemResponse
require.NoError(t, json.Unmarshal(data, &items))
require.Len(t, items.Value, 1)
require.Equal(t, "file", items.Value[0].ItemType)
require.Equal(t, "my-artifact/data.txt", items.Value[0].Path)
status, data = request(t, http.MethodGet, items.Value[0].ContentLocation, nil, nil)
require.Equal(t, http.StatusOK, status)
require.Equal(t, content, string(data))
stored, err := os.ReadFile(filepath.Join(artifactPath, runID, "my-artifact", "data.txt"))
require.NoError(t, err)
require.Equal(t, content, string(stored))
})
t.Run("gzip-roundtrip", func(t *testing.T) {
const runID, item, content = "2", "logs/app.log", "compressed payload\n"
var buf bytes.Buffer
gz := gzip.NewWriter(&buf)
_, err := gz.Write([]byte(content))
require.NoError(t, err)
require.NoError(t, gz.Close())
status, data := request(t, http.MethodPut, baseURL+"/upload/"+runID+"?itemPath="+url.QueryEscape(item),
&buf, http.Header{"Content-Encoding": []string{"gzip"}})
require.Equal(t, http.StatusOK, status, string(data))
// stored compressed, with the server's gzip marker suffix
_, err = os.Stat(filepath.Join(artifactPath, runID, "logs", "app.log.gz__"))
require.NoError(t, err)
status, data = request(t, http.MethodGet, baseURL+"/download/"+runID+"?itemPath=logs", nil, nil)
require.Equal(t, http.StatusOK, status, string(data))
var items ContainerItemResponse
require.NoError(t, json.Unmarshal(data, &items))
require.Len(t, items.Value, 1)
require.Equal(t, "logs/app.log", items.Value[0].Path)
status, data = request(t, http.MethodGet, items.Value[0].ContentLocation, nil, nil)
require.Equal(t, http.StatusOK, status)
require.Equal(t, content, string(data))
})
// GHSL-2023-004: an itemPath that climbs out of the run directory must be neutralised so the
// blob cannot be written outside the artifact root.
t.Run("GHSL-2023-004", func(t *testing.T) {
const runID, content = "3", "contained\n"
status, data := request(t, http.MethodPut, baseURL+"/upload/"+runID+"?itemPath="+url.QueryEscape("../../escape.txt"),
strings.NewReader(content), nil)
require.Equal(t, http.StatusOK, status, string(data))
stored, err := os.ReadFile(filepath.Join(artifactPath, runID, "escape.txt"))
require.NoError(t, err)
require.Equal(t, content, string(stored))
_, err = os.Stat(filepath.Join(filepath.Dir(artifactPath), "escape.txt"))
require.True(t, os.IsNotExist(err), "upload escaped the artifact root")
status, data = request(t, http.MethodGet, baseURL+"/artifact/"+runID+"/escape.txt", nil, nil)
require.Equal(t, http.StatusOK, status)
require.Equal(t, content, string(data))
})
}
func TestMkdirFsImplSafeResolve(t *testing.T) {
assert := assert.New(t)
baseDir := "/foo/bar"
tests := map[string]struct {
input string
want string
}{
"simple": {input: "baz", want: "/foo/bar/baz"},
"nested": {input: "baz/blue", want: "/foo/bar/baz/blue"},
"dots in middle": {input: "baz/../../blue", want: "/foo/bar/blue"},
"leading dots": {input: "../../parent", want: "/foo/bar/parent"},
"root path": {input: "/root", want: "/foo/bar/root"},
"root": {input: "/", want: "/foo/bar"},
"empty": {input: "", want: "/foo/bar"},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
assert.Equal(tc.want, safeResolve(baseDir, tc.input))
})
}
}
func TestDownloadArtifactFileUnsafePath(t *testing.T) {
assert := assert.New(t)
memfs := fstest.MapFS(map[string]*fstest.MapFile{
"artifact/server/path/some/file": {
Data: []byte("content"),
},
})
router := httprouter.New()
downloads(router, "artifact/server/path", memfs)
req, _ := http.NewRequest(http.MethodGet, "http://localhost/artifact/2/../../some/file", nil)
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
assert.FailNow(fmt.Sprintf("Wrong status: %d", status))
}
data := rr.Body.Bytes()
assert.Equal("content", string(data))
}
func TestArtifactUploadBlobUnsafePath(t *testing.T) {
assert := assert.New(t)
memfs := fstest.MapFS(map[string]*fstest.MapFile{})
router := httprouter.New()
uploads(router, "artifact/server/path", writeMapFS{memfs})
req, _ := http.NewRequest(http.MethodPut, "http://localhost/upload/1?itemPath=../../some/file", strings.NewReader("content"))
rr := httptest.NewRecorder()
router.ServeHTTP(rr, req)
if status := rr.Code; status != http.StatusOK {
assert.Fail("Wrong status")
}
response := ResponseMessage{}
err := json.Unmarshal(rr.Body.Bytes(), &response)
if err != nil {
panic(err)
}
assert.Equal("success", response.Message)
assert.Equal("content", string(memfs["artifact/server/path/1/some/file"].Data))
}

View File

@@ -1,9 +1,15 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import "slices"
// CartesianProduct takes map of lists and returns list of unique tuples
func CartesianProduct(mapOfLists map[string][]interface{}) []map[string]interface{} {
func CartesianProduct(mapOfLists map[string][]any) []map[string]any {
listNames := make([]string, 0)
lists := make([][]interface{}, 0)
lists := make([][]any, 0)
for k, v := range mapOfLists {
listNames = append(listNames, k)
lists = append(lists, v)
@@ -11,9 +17,9 @@ func CartesianProduct(mapOfLists map[string][]interface{}) []map[string]interfac
listCart := cartN(lists...)
rtn := make([]map[string]interface{}, 0)
rtn := make([]map[string]any, 0)
for _, list := range listCart {
vMap := make(map[string]interface{})
vMap := make(map[string]any)
for i, v := range list {
vMap[listNames[i]] = v
}
@@ -22,7 +28,7 @@ func CartesianProduct(mapOfLists map[string][]interface{}) []map[string]interfac
return rtn
}
func cartN(a ...[]interface{}) [][]interface{} {
func cartN(a ...[]any) [][]any {
c := 1
for _, a := range a {
c *= len(a)
@@ -30,8 +36,8 @@ func cartN(a ...[]interface{}) [][]interface{} {
if c == 0 || len(a) == 0 {
return nil
}
p := make([][]interface{}, c)
b := make([]interface{}, c*len(a))
p := make([][]any, c)
b := make([]any, c*len(a))
n := make([]int, len(a))
s := 0
for i := range p {
@@ -42,7 +48,7 @@ func cartN(a ...[]interface{}) [][]interface{} {
for j, n := range n {
pi[j] = a[j][n]
}
for j := len(n) - 1; j >= 0; j-- {
for j := range slices.Backward(n) {
n[j]++
if n[j] < len(a[j]) {
break

View File

@@ -1,3 +1,7 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
@@ -8,7 +12,7 @@ import (
func TestCartesianProduct(t *testing.T) {
assert := assert.New(t)
input := map[string][]interface{}{
input := map[string][]any{
"foo": {1, 2, 3, 4},
"bar": {"a", "b", "c"},
"baz": {false, true},
@@ -25,15 +29,15 @@ func TestCartesianProduct(t *testing.T) {
assert.Contains(v, "baz")
}
input = map[string][]interface{}{
input = map[string][]any{
"foo": {1, 2, 3, 4},
"bar": {},
"baz": {false, true},
}
output = CartesianProduct(input)
assert.Len(output, 0)
assert.Empty(output)
input = map[string][]interface{}{}
input = map[string][]any{}
output = CartesianProduct(input)
assert.Len(output, 0)
assert.Empty(output)
}

View File

@@ -1,3 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (

View File

@@ -1,30 +1,17 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
"context"
"fmt"
"runtime/debug"
log "github.com/sirupsen/logrus"
)
// Warning that implements `error` but safe to ignore
type Warning struct {
Message string
}
// Error the contract for error
func (w Warning) Error() string {
return w.Message
}
// Warningf create a warning
func Warningf(format string, args ...interface{}) Warning {
w := Warning{
Message: fmt.Sprintf(format, args...),
}
return w
}
// Executor define contract for the steps of a workflow
type Executor func(ctx context.Context) error
@@ -32,7 +19,7 @@ type Executor func(ctx context.Context) error
type Conditional func(ctx context.Context) bool
// NewInfoExecutor is an executor that logs messages
func NewInfoExecutor(format string, args ...interface{}) Executor {
func NewInfoExecutor(format string, args ...any) Executor {
return func(ctx context.Context) error {
logger := Logger(ctx)
logger.Infof(format, args...)
@@ -41,7 +28,7 @@ func NewInfoExecutor(format string, args ...interface{}) Executor {
}
// NewDebugExecutor is an executor that logs messages
func NewDebugExecutor(format string, args ...interface{}) Executor {
func NewDebugExecutor(format string, args ...any) Executor {
return func(ctx context.Context) error {
logger := Logger(ctx)
logger.Debugf(format, args...)
@@ -68,7 +55,7 @@ func NewPipelineExecutor(executors ...Executor) Executor {
}
// NewConditionalExecutor creates a new executor based on conditions
func NewConditionalExecutor(conditional Conditional, trueExecutor Executor, falseExecutor Executor) Executor {
func NewConditionalExecutor(conditional Conditional, trueExecutor, falseExecutor Executor) Executor {
return func(ctx context.Context) error {
if conditional(ctx) {
if trueExecutor != nil {
@@ -92,31 +79,55 @@ func NewErrorExecutor(err error) Executor {
// NewParallelExecutor creates a new executor from a parallel of other executors
func NewParallelExecutor(parallel int, executors ...Executor) Executor {
if len(executors) == 0 {
return func(ctx context.Context) error {
return ctx.Err()
}
}
return func(ctx context.Context) error {
work := make(chan Executor, len(executors))
errs := make(chan error, len(executors))
if 1 > parallel {
log.Infof("Parallel tasks (%d) below minimum, setting to 1", parallel)
log.Debugf("Parallel tasks (%d) below minimum, setting to 1", parallel)
parallel = 1
}
log.Infof("NewParallelExecutor: Creating %d workers for %d executors", parallel, len(executors))
for i := 0; i < parallel; i++ {
go func(work <-chan Executor, errs chan<- error) {
go func(workerID int, work <-chan Executor, errs chan<- error) {
log.Debugf("Worker %d started", workerID)
taskCount := 0
for executor := range work {
errs <- executor(ctx)
taskCount++
log.Debugf("Worker %d executing task %d", workerID, taskCount)
// Recover from panics in executors to avoid crashing the worker
// goroutine which would leave the runner process hung.
// https://gitea.com/gitea/runner/issues/371
errs <- func() (err error) {
defer func() {
if r := recover(); r != nil {
log.Errorf("panic in executor: %v\n%s", r, debug.Stack())
err = fmt.Errorf("panic: %v", r)
}
}()
return executor(ctx)
}()
}
}(work, errs)
log.Debugf("Worker %d finished (%d tasks executed)", workerID, taskCount)
}(i, work, errs)
}
for i := 0; i < len(executors); i++ {
for i := range executors {
work <- executors[i]
}
close(work)
// Executor waits all executors to cleanup these resources.
var firstErr error
for i := 0; i < len(executors); i++ {
for range executors {
err := <-errs
if firstErr == nil {
firstErr = err
@@ -133,14 +144,8 @@ func NewParallelExecutor(parallel int, executors ...Executor) Executor {
// Then runs another executor if this executor succeeds
func (e Executor) Then(then Executor) Executor {
return func(ctx context.Context) error {
err := e(ctx)
if err != nil {
switch err.(type) {
case Warning:
Logger(ctx).Warning(err.Error())
default:
return err
}
if err := e(ctx); err != nil {
return err
}
if ctx.Err() != nil {
return ctx.Err()

View File

@@ -0,0 +1,89 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
"context"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// Simple fast test that verifies max-parallel: 2 limits concurrency
func TestMaxParallel2Quick(t *testing.T) {
ctx := context.Background()
var currentRunning atomic.Int32
var maxSimultaneous atomic.Int32
executors := make([]Executor, 4)
for i := range 4 {
executors[i] = func(ctx context.Context) error {
current := currentRunning.Add(1)
// Update max if needed
for {
maxValue := maxSimultaneous.Load()
if current <= maxValue || maxSimultaneous.CompareAndSwap(maxValue, current) {
break
}
}
time.Sleep(10 * time.Millisecond)
currentRunning.Add(-1)
return nil
}
}
err := NewParallelExecutor(2, executors...)(ctx)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.LessOrEqual(t, maxSimultaneous.Load(), int32(2),
"Should not exceed max-parallel: 2")
}
// Test that verifies max-parallel: 1 enforces sequential execution
func TestMaxParallel1Sequential(t *testing.T) {
ctx := context.Background()
var currentRunning atomic.Int32
var maxSimultaneous atomic.Int32
var executionOrder []int
var orderMutex sync.Mutex
executors := make([]Executor, 5)
for i := range 5 {
taskID := i
executors[i] = func(ctx context.Context) error {
current := currentRunning.Add(1)
// Track execution order
orderMutex.Lock()
executionOrder = append(executionOrder, taskID)
orderMutex.Unlock()
// Update max if needed
for {
maxValue := maxSimultaneous.Load()
if current <= maxValue || maxSimultaneous.CompareAndSwap(maxValue, current) {
break
}
}
time.Sleep(20 * time.Millisecond)
currentRunning.Add(-1)
return nil
}
}
err := NewParallelExecutor(1, executors...)(ctx)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, int32(1), maxSimultaneous.Load(),
"max-parallel: 1 should only run 1 task at a time")
assert.Len(t, executionOrder, 5, "All 5 tasks should have executed")
}

View File

@@ -0,0 +1,221 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
"context"
"sync"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
// TestMaxParallelJobExecution tests actual job execution with max-parallel
func TestMaxParallelJobExecution(t *testing.T) {
t.Run("MaxParallel=1 Sequential", func(t *testing.T) {
var currentRunning atomic.Int32
var maxConcurrent int32
var executionOrder []int
var mu sync.Mutex
executors := make([]Executor, 5)
for i := range 5 {
taskID := i
executors[i] = func(ctx context.Context) error {
current := currentRunning.Add(1)
// Track max concurrent
for {
maxValue := atomic.LoadInt32(&maxConcurrent)
if current <= maxValue || atomic.CompareAndSwapInt32(&maxConcurrent, maxValue, current) {
break
}
}
mu.Lock()
executionOrder = append(executionOrder, taskID)
mu.Unlock()
time.Sleep(10 * time.Millisecond)
currentRunning.Add(-1)
return nil
}
}
ctx := context.Background()
err := NewParallelExecutor(1, executors...)(ctx)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, int32(1), maxConcurrent, "Should never exceed 1 concurrent execution")
assert.Len(t, executionOrder, 5, "All tasks should execute")
})
t.Run("MaxParallel=3 Limited", func(t *testing.T) {
var currentRunning atomic.Int32
var maxConcurrent int32
executors := make([]Executor, 10)
for i := range 10 {
executors[i] = func(ctx context.Context) error {
current := currentRunning.Add(1)
for {
maxValue := atomic.LoadInt32(&maxConcurrent)
if current <= maxValue || atomic.CompareAndSwapInt32(&maxConcurrent, maxValue, current) {
break
}
}
time.Sleep(20 * time.Millisecond)
currentRunning.Add(-1)
return nil
}
}
ctx := context.Background()
err := NewParallelExecutor(3, executors...)(ctx)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.LessOrEqual(t, int(maxConcurrent), 3, "Should never exceed 3 concurrent executions")
assert.GreaterOrEqual(t, int(maxConcurrent), 1, "Should have at least 1 concurrent execution")
})
t.Run("MaxParallel=0 Uses1Worker", func(t *testing.T) {
var maxConcurrent int32
var currentRunning atomic.Int32
executors := make([]Executor, 5)
for i := range 5 {
executors[i] = func(ctx context.Context) error {
current := currentRunning.Add(1)
for {
maxValue := atomic.LoadInt32(&maxConcurrent)
if current <= maxValue || atomic.CompareAndSwapInt32(&maxConcurrent, maxValue, current) {
break
}
}
time.Sleep(10 * time.Millisecond)
currentRunning.Add(-1)
return nil
}
}
ctx := context.Background()
// When maxParallel is 0 or negative, it defaults to 1
err := NewParallelExecutor(0, executors...)(ctx)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, int32(1), maxConcurrent, "Should use 1 worker when max-parallel is 0")
})
}
// TestMaxParallelWithErrors tests error handling with max-parallel
func TestMaxParallelWithErrors(t *testing.T) {
t.Run("OneTaskFailsOthersContinue", func(t *testing.T) {
var successCount int32
executors := make([]Executor, 5)
for i := range 5 {
taskID := i
executors[i] = func(ctx context.Context) error {
if taskID == 2 {
return assert.AnError
}
atomic.AddInt32(&successCount, 1)
return nil
}
}
ctx := context.Background()
err := NewParallelExecutor(2, executors...)(ctx)
// Should return the error from task 2
assert.Error(t, err) //nolint:testifylint // pre-existing issue from nektos/act
// Other tasks should still execute
assert.Equal(t, int32(4), successCount, "4 tasks should succeed")
})
t.Run("ContextCancellation", func(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
var startedCount int32
executors := make([]Executor, 10)
for i := range 10 {
executors[i] = func(ctx context.Context) error {
atomic.AddInt32(&startedCount, 1)
time.Sleep(100 * time.Millisecond)
return nil
}
}
// Cancel after a short delay
go func() {
time.Sleep(30 * time.Millisecond)
cancel()
}()
err := NewParallelExecutor(3, executors...)(ctx)
assert.Error(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.ErrorIs(t, err, context.Canceled) //nolint:testifylint // pre-existing issue from nektos/act
// Not all tasks should start due to cancellation (but timing may vary)
// Just verify cancellation occurred
t.Logf("Started %d tasks before cancellation", startedCount)
})
}
// TestMaxParallelResourceSharing tests resource sharing scenarios
func TestMaxParallelResourceSharing(t *testing.T) {
t.Run("SharedResourceWithMutex", func(t *testing.T) {
var sharedCounter int
var mu sync.Mutex
executors := make([]Executor, 100)
for i := range 100 {
executors[i] = func(ctx context.Context) error {
mu.Lock()
sharedCounter++
mu.Unlock()
return nil
}
}
ctx := context.Background()
err := NewParallelExecutor(10, executors...)(ctx)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, 100, sharedCounter, "All tasks should increment counter")
})
t.Run("ChannelCommunication", func(t *testing.T) {
resultChan := make(chan int, 50)
executors := make([]Executor, 50)
for i := range 50 {
taskID := i
executors[i] = func(ctx context.Context) error {
resultChan <- taskID
return nil
}
}
ctx := context.Background()
err := NewParallelExecutor(5, executors...)(ctx)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
close(resultChan)
results := make(map[int]bool)
for result := range resultChan {
results[result] = true
}
assert.Len(t, results, 50, "All task IDs should be received")
})
}

View File

@@ -1,12 +1,18 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
"context"
"fmt"
"errors"
"sync/atomic"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewWorkflow(t *testing.T) {
@@ -16,11 +22,11 @@ func TestNewWorkflow(t *testing.T) {
// empty
emptyWorkflow := NewPipelineExecutor()
assert.Nil(emptyWorkflow(ctx))
assert.NoError(emptyWorkflow(ctx)) //nolint:testifylint // pre-existing issue from nektos/act
// error case
errorWorkflow := NewErrorExecutor(fmt.Errorf("test error"))
assert.NotNil(errorWorkflow(ctx))
errorWorkflow := NewErrorExecutor(errors.New("test error"))
assert.Error(errorWorkflow(ctx)) //nolint:testifylint // pre-existing issue from nektos/act
// multiple success case
runcount := 0
@@ -33,7 +39,7 @@ func TestNewWorkflow(t *testing.T) {
runcount++
return nil
})
assert.Nil(successWorkflow(ctx))
assert.NoError(successWorkflow(ctx)) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(2, runcount)
}
@@ -55,7 +61,7 @@ func TestNewConditionalExecutor(t *testing.T) {
return nil
})(ctx)
assert.Nil(err)
assert.NoError(err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(0, trueCount)
assert.Equal(1, falseCount)
@@ -69,7 +75,7 @@ func TestNewConditionalExecutor(t *testing.T) {
return nil
})(ctx)
assert.Nil(err)
assert.NoError(err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(1, trueCount)
assert.Equal(1, falseCount)
}
@@ -79,38 +85,52 @@ func TestNewParallelExecutor(t *testing.T) {
ctx := context.Background()
count := 0
activeCount := 0
maxCount := 0
var count, activeCount, maxCount atomic.Int32
emptyWorkflow := NewPipelineExecutor(func(ctx context.Context) error {
count++
count.Add(1)
activeCount++
if activeCount > maxCount {
maxCount = activeCount
active := activeCount.Add(1)
for {
m := maxCount.Load()
if active <= m || maxCount.CompareAndSwap(m, active) {
break
}
}
time.Sleep(2 * time.Second)
activeCount--
activeCount.Add(-1)
return nil
})
err := NewParallelExecutor(2, emptyWorkflow, emptyWorkflow, emptyWorkflow)(ctx)
assert.Equal(3, count, "should run all 3 executors")
assert.Equal(2, maxCount, "should run at most 2 executors in parallel")
assert.Nil(err)
assert.Equal(int32(3), count.Load(), "should run all 3 executors")
assert.Equal(int32(2), maxCount.Load(), "should run at most 2 executors in parallel")
assert.NoError(err) //nolint:testifylint // pre-existing issue from nektos/act
// Reset to test running the executor with 0 parallelism
count = 0
activeCount = 0
maxCount = 0
count.Store(0)
activeCount.Store(0)
maxCount.Store(0)
errSingle := NewParallelExecutor(0, emptyWorkflow, emptyWorkflow, emptyWorkflow)(ctx)
assert.Equal(3, count, "should run all 3 executors")
assert.Equal(1, maxCount, "should run at most 1 executors in parallel")
assert.Nil(errSingle)
assert.Equal(int32(3), count.Load(), "should run all 3 executors")
assert.Equal(int32(1), maxCount.Load(), "should run at most 1 executors in parallel")
assert.NoError(errSingle)
}
func TestNewParallelExecutorEmpty(t *testing.T) {
assert := assert.New(t)
ctx := context.Background()
require.NoError(t, NewParallelExecutor(2)(ctx))
canceledCtx, cancel := context.WithCancel(context.Background())
cancel()
err := NewParallelExecutor(2)(canceledCtx)
assert.ErrorIs(err, context.Canceled)
}
func TestNewParallelExecutorFailed(t *testing.T) {
@@ -122,7 +142,7 @@ func TestNewParallelExecutorFailed(t *testing.T) {
count := 0
errorWorkflow := NewPipelineExecutor(func(ctx context.Context) error {
count++
return fmt.Errorf("fake error")
return errors.New("fake error")
})
err := NewParallelExecutor(1, errorWorkflow)(ctx)
assert.Equal(1, count)
@@ -135,18 +155,18 @@ func TestNewParallelExecutorCanceled(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
cancel()
errExpected := fmt.Errorf("fake error")
errExpected := errors.New("fake error")
count := 0
var count atomic.Int32
successWorkflow := NewPipelineExecutor(func(ctx context.Context) error {
count++
count.Add(1)
return nil
})
errorWorkflow := NewPipelineExecutor(func(ctx context.Context) error {
count++
count.Add(1)
return errExpected
})
err := NewParallelExecutor(3, errorWorkflow, successWorkflow, successWorkflow)(ctx)
assert.Equal(3, count)
assert.Error(errExpected, err)
assert.Equal(int32(3), count.Load())
assert.Error(errExpected, err) //nolint:testifylint // pre-existing issue from nektos/act
}

View File

@@ -1,3 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2022 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
@@ -11,6 +15,8 @@ import (
"strings"
"sync"
"gitea.com/gitea/runner/act/common"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/config"
"github.com/go-git/go-git/v5/plumbing"
@@ -18,8 +24,6 @@ import (
"github.com/go-git/go-git/v5/plumbing/transport/http"
"github.com/mattn/go-isatty"
log "github.com/sirupsen/logrus"
"github.com/nektos/act/pkg/common"
)
var (
@@ -28,12 +32,23 @@ var (
githubHTTPRegex = regexp.MustCompile(`^https?://.*github.com.*/(.+)/(.+?)(?:.git)?$`)
githubSSHRegex = regexp.MustCompile(`github.com[:/](.+)/(.+?)(?:.git)?$`)
cloneLock sync.Mutex
cloneLocks sync.Map // key: clone target directory; value: *sync.Mutex
ErrShortRef = errors.New("short SHA references are not supported")
ErrNoRepo = errors.New("unable to find git repo")
)
// AcquireCloneLock returns an unlock function after locking the per-directory mutex for dir.
// Only concurrent operations targeting the same directory are serialized; clones into different directories run in parallel.
// Callers reading files inside dir (e.g. tarring a checked-out action into a job container) must hold this lock too,
// otherwise a concurrent NewGitCloneExecutor on the same dir can mutate the worktree mid-read.
func AcquireCloneLock(dir string) func() {
v, _ := cloneLocks.LoadOrStore(dir, &sync.Mutex{})
mu := v.(*sync.Mutex)
mu.Lock()
return mu.Unlock
}
type Error struct {
err error
commit string
@@ -51,8 +66,21 @@ func (e *Error) Commit() string {
return e.commit
}
// goGitMu serializes go-git repository access across the process. go-git is not safe for
// concurrent use of the same repository (even read access decodes packfiles into shared
// state), so parallel jobs inspecting the shared workdir repo race without this. The guarded
// operations are fast local reads; gitea runs one job per process, so the lock is effectively
// uncontended in production.
var goGitMu sync.Mutex
// FindGitRevision get the current git revision
func FindGitRevision(ctx context.Context, file string) (shortSha string, sha string, err error) {
func FindGitRevision(ctx context.Context, file string) (shortSha, sha string, err error) {
goGitMu.Lock()
defer goGitMu.Unlock()
return findGitRevision(ctx, file)
}
func findGitRevision(ctx context.Context, file string) (shortSha, sha string, err error) {
logger := common.Logger(ctx)
gitDir, err := git.PlainOpenWithOptions(
@@ -62,7 +90,6 @@ func FindGitRevision(ctx context.Context, file string) (shortSha string, sha str
EnableDotGitCommonDir: true,
},
)
if err != nil {
logger.WithError(err).Error("path", file, "not located inside a git repository")
return "", "", err
@@ -74,7 +101,7 @@ func FindGitRevision(ctx context.Context, file string) (shortSha string, sha str
}
if head.Hash().IsZero() {
return "", "", fmt.Errorf("HEAD sha1 could not be resolved")
return "", "", errors.New("HEAD sha1 could not be resolved")
}
hash := head.Hash().String()
@@ -85,10 +112,13 @@ func FindGitRevision(ctx context.Context, file string) (shortSha string, sha str
// FindGitRef get the current git ref
func FindGitRef(ctx context.Context, file string) (string, error) {
goGitMu.Lock()
defer goGitMu.Unlock()
logger := common.Logger(ctx)
logger.Debugf("Loading revision from git directory")
_, ref, err := FindGitRevision(ctx, file)
_, ref, err := findGitRevision(ctx, file)
if err != nil {
return "", err
}
@@ -96,8 +126,8 @@ func FindGitRef(ctx context.Context, file string) (string, error) {
logger.Debugf("HEAD points to '%s'", ref)
// Prefer the git library to iterate over the references and find a matching tag or branch.
var refTag = ""
var refBranch = ""
refTag := ""
refBranch := ""
repo, err := git.PlainOpenWithOptions(
file,
&git.PlainOpenOptions{
@@ -105,7 +135,6 @@ func FindGitRef(ctx context.Context, file string) (string, error) {
EnableDotGitCommonDir: true,
},
)
if err != nil {
return "", err
}
@@ -144,7 +173,6 @@ func FindGitRef(ctx context.Context, file string) (string, error) {
return nil
})
if err != nil {
return "", err
}
@@ -162,6 +190,8 @@ func FindGitRef(ctx context.Context, file string) (string, error) {
// FindGithubRepo get the repo
func FindGithubRepo(ctx context.Context, file, githubInstance, remoteName string) (string, error) {
goGitMu.Lock()
defer goGitMu.Unlock()
if remoteName == "" {
remoteName = "origin"
}
@@ -198,7 +228,7 @@ func findGitRemoteURL(_ context.Context, file, remoteName string) (string, error
return remote.Config().URLs[0], nil
}
func findGitSlug(url string, githubInstance string) (string, string, error) {
func findGitSlug(url, githubInstance string) (string, string, error) { //nolint:unparam // pre-existing issue from nektos/act
if matches := codeCommitHTTPRegex.FindStringSubmatch(url); matches != nil {
return "CodeCommit", matches[2], nil
} else if matches := codeCommitSSHRegex.FindStringSubmatch(url); matches != nil {
@@ -209,7 +239,7 @@ func findGitSlug(url string, githubInstance string) (string, string, error) {
return "GitHub", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
} else if githubInstance != "github.com" {
gheHTTPRegex := regexp.MustCompile(fmt.Sprintf(`^https?://%s/(.+)/(.+?)(?:.git)?$`, githubInstance))
gheSSHRegex := regexp.MustCompile(fmt.Sprintf(`%s[:/](.+)/(.+?)(?:.git)?$`, githubInstance))
gheSSHRegex := regexp.MustCompile(githubInstance + "[:/](.+)/(.+?)(?:.git)?$")
if matches := gheHTTPRegex.FindStringSubmatch(url); matches != nil {
return "GitHubEnterprise", fmt.Sprintf("%s/%s", matches[1], matches[2]), nil
} else if matches := gheSSHRegex.FindStringSubmatch(url); matches != nil {
@@ -221,55 +251,65 @@ func findGitSlug(url string, githubInstance string) (string, string, error) {
// NewGitCloneExecutorInput the input for the NewGitCloneExecutor
type NewGitCloneExecutorInput struct {
URL string
Ref string
Dir string
Token string
URL string
Ref string
Dir string
Token string
OfflineMode bool
// For Gitea
InsecureSkipTLS bool
}
// CloneIfRequired ...
func CloneIfRequired(ctx context.Context, refName plumbing.ReferenceName, input NewGitCloneExecutorInput, logger log.FieldLogger) (*git.Repository, error) {
// CloneIfRequired returns the repository and a boolean indicating whether an existing local clone was reused.
func CloneIfRequired(ctx context.Context, refName plumbing.ReferenceName, input NewGitCloneExecutorInput, logger log.FieldLogger) (*git.Repository, bool, error) {
r, err := git.PlainOpen(input.Dir)
if err != nil {
var progressWriter io.Writer
if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {
if entry, ok := logger.(*log.Entry); ok {
progressWriter = entry.WriterLevel(log.DebugLevel)
} else if lgr, ok := logger.(*log.Logger); ok {
progressWriter = lgr.WriterLevel(log.DebugLevel)
} else {
log.Errorf("Unable to get writer from logger (type=%T)", logger)
progressWriter = os.Stdout
}
}
if err == nil {
// Reuse existing clone
return r, true, nil
}
cloneOptions := git.CloneOptions{
URL: input.URL,
Progress: progressWriter,
}
if input.Token != "" {
cloneOptions.Auth = &http.BasicAuth{
Username: "token",
Password: input.Token,
}
}
r, err = git.PlainCloneContext(ctx, input.Dir, false, &cloneOptions)
if err != nil {
logger.Errorf("Unable to clone %v %s: %v", input.URL, refName, err)
return nil, err
}
if err = os.Chmod(input.Dir, 0o755); err != nil {
return nil, err
var progressWriter io.Writer
if isatty.IsTerminal(os.Stdout.Fd()) || isatty.IsCygwinTerminal(os.Stdout.Fd()) {
if entry, ok := logger.(*log.Entry); ok {
progressWriter = entry.WriterLevel(log.DebugLevel)
} else if lgr, ok := logger.(*log.Logger); ok {
progressWriter = lgr.WriterLevel(log.DebugLevel)
} else {
log.Errorf("Unable to get writer from logger (type=%T)", logger)
progressWriter = os.Stdout
}
}
return r, nil
cloneOptions := git.CloneOptions{
URL: input.URL,
Progress: progressWriter,
InsecureSkipTLS: input.InsecureSkipTLS, // For Gitea
}
if input.Token != "" {
cloneOptions.Auth = &http.BasicAuth{
Username: "token",
Password: input.Token,
}
}
r, err = git.PlainCloneContext(ctx, input.Dir, false, &cloneOptions)
if err != nil {
logger.Errorf("Unable to clone %v %s: %v", input.URL, refName, err)
return nil, false, err
}
if err = os.Chmod(input.Dir, 0o755); err != nil {
return nil, false, err
}
return r, false, nil
}
func gitOptions(token string) (fetchOptions git.FetchOptions, pullOptions git.PullOptions) {
fetchOptions.RefSpecs = []config.RefSpec{"refs/*:refs/*", "HEAD:refs/heads/HEAD"}
fetchOptions.Force = true
pullOptions.Force = true
if token != "" {
@@ -285,38 +325,44 @@ func gitOptions(token string) (fetchOptions git.FetchOptions, pullOptions git.Pu
}
// NewGitCloneExecutor creates an executor to clone git repos
//
//nolint:gocyclo
func NewGitCloneExecutor(input NewGitCloneExecutorInput) common.Executor {
return func(ctx context.Context) error {
logger := common.Logger(ctx)
logger.Infof(" \u2601 git clone '%s' # ref=%s", input.URL, input.Ref)
logger.Infof("git clone '%s' # ref=%s", input.URL, input.Ref)
logger.Debugf(" cloning %s to %s", input.URL, input.Dir)
cloneLock.Lock()
defer cloneLock.Unlock()
defer AcquireCloneLock(input.Dir)()
refName := plumbing.ReferenceName(fmt.Sprintf("refs/heads/%s", input.Ref))
r, err := CloneIfRequired(ctx, refName, input, logger)
refName := plumbing.ReferenceName("refs/heads/" + input.Ref)
r, reused, err := CloneIfRequired(ctx, refName, input, logger)
if err != nil {
return err
}
isOfflineMode := input.OfflineMode
// fetch latest changes
fetchOptions, pullOptions := gitOptions(input.Token)
err = r.Fetch(&fetchOptions)
if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
return err
if input.InsecureSkipTLS { // For Gitea
fetchOptions.InsecureSkipTLS = true
pullOptions.InsecureSkipTLS = true
}
if !isOfflineMode {
err = r.Fetch(&fetchOptions)
if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
return err
}
}
var hash *plumbing.Hash
rev := plumbing.Revision(input.Ref)
if hash, err = r.ResolveRevision(rev); err != nil {
// ResolveRevision returns a nil hash on error, and a branch ref legitimately fails
// here (no local refs/heads/<ref>); the duck-typing below resolves it.
logger.Errorf("Unable to resolve %s: %v", input.Ref, err)
}
if hash.String() != input.Ref && strings.HasPrefix(hash.String(), input.Ref) {
} else if hash.String() != input.Ref && strings.HasPrefix(hash.String(), input.Ref) {
return &Error{
err: ErrShortRef,
commit: hash.String(),
@@ -368,10 +414,17 @@ func NewGitCloneExecutor(input NewGitCloneExecutorInput) common.Executor {
}
}
if err = w.Pull(&pullOptions); err != nil && err != git.NoErrAlreadyUpToDate {
logger.Debugf("Unable to pull %s: %v", refName, err)
reusedMsg := ""
if !isOfflineMode {
if err = w.Pull(&pullOptions); err != nil && err != git.NoErrAlreadyUpToDate {
logger.Debugf("Unable to pull %s: %v", refName, err)
}
} else if reused {
reusedMsg = " (reused in offline mode)"
}
logger.Debugf("Cloned %s to %s", input.URL, input.Dir)
logger.Debugf("Cloned %s to %s%s", input.URL, input.Dir, reusedMsg)
if hash.String() != input.Ref && refType == "branch" {
logger.Debugf("Provided ref is not a sha. Updating branch ref after pull")

420
act/common/git/git_test.go Normal file
View File

@@ -0,0 +1,420 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2022 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package git
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
"sync"
"syscall"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFindGitSlug(t *testing.T) {
assert := assert.New(t)
slugTests := []struct {
url string // input
provider string // expected result
slug string // expected result
}{
{"https://git-codecommit.us-east-1.amazonaws.com/v1/repos/my-repo-name", "CodeCommit", "my-repo-name"},
{"ssh://git-codecommit.us-west-2.amazonaws.com/v1/repos/my-repo", "CodeCommit", "my-repo"},
{"git@github.com:nektos/act.git", "GitHub", "nektos/act"},
{"git@github.com:nektos/act", "GitHub", "nektos/act"},
{"https://github.com/nektos/act.git", "GitHub", "nektos/act"},
{"http://github.com/nektos/act.git", "GitHub", "nektos/act"},
{"https://github.com/nektos/act", "GitHub", "nektos/act"},
{"http://github.com/nektos/act", "GitHub", "nektos/act"},
{"git+ssh://git@github.com/owner/repo.git", "GitHub", "owner/repo"},
{"http://myotherrepo.com/act.git", "", "http://myotherrepo.com/act.git"},
}
for _, tt := range slugTests {
provider, slug, err := findGitSlug(tt.url, "github.com")
assert.NoError(err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(tt.provider, provider)
assert.Equal(tt.slug, slug)
}
}
func cleanGitHooks(dir string) error {
hooksDir := filepath.Join(dir, ".git", "hooks")
files, err := os.ReadDir(hooksDir)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
for _, f := range files {
if f.IsDir() {
continue
}
relName := filepath.Join(hooksDir, f.Name())
if err := os.Remove(relName); err != nil {
return err
}
}
return nil
}
func TestFindGitRemoteURL(t *testing.T) {
assert := assert.New(t)
basedir := t.TempDir()
err := gitCmd("init", basedir)
assert.NoError(err) //nolint:testifylint // pre-existing issue from nektos/act
err = cleanGitHooks(basedir)
assert.NoError(err) //nolint:testifylint // pre-existing issue from nektos/act
remoteURL := "https://git-codecommit.us-east-1.amazonaws.com/v1/repos/my-repo-name"
err = gitCmd("-C", basedir, "remote", "add", "origin", remoteURL)
assert.NoError(err) //nolint:testifylint // pre-existing issue from nektos/act
u, err := findGitRemoteURL(context.Background(), basedir, "origin")
assert.NoError(err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(remoteURL, u)
remoteURL = "git@github.com/AwesomeOwner/MyAwesomeRepo.git"
err = gitCmd("-C", basedir, "remote", "add", "upstream", remoteURL)
assert.NoError(err) //nolint:testifylint // pre-existing issue from nektos/act
u, err = findGitRemoteURL(context.Background(), basedir, "upstream")
assert.NoError(err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(remoteURL, u)
}
func TestGitFindRef(t *testing.T) {
basedir := t.TempDir()
for name, tt := range map[string]struct {
Prepare func(t *testing.T, dir string)
Assert func(t *testing.T, ref string, err error)
}{
"new_repo": {
Prepare: func(t *testing.T, dir string) {},
Assert: func(t *testing.T, ref string, err error) {
require.Error(t, err)
},
},
"new_repo_with_commit": {
Prepare: func(t *testing.T, dir string) {
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "msg"))
},
Assert: func(t *testing.T, ref string, err error) {
require.NoError(t, err)
require.Equal(t, "refs/heads/master", ref)
},
},
"current_head_is_tag": {
Prepare: func(t *testing.T, dir string) {
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "commit msg"))
require.NoError(t, gitCmd("-C", dir, "tag", "v1.2.3"))
require.NoError(t, gitCmd("-C", dir, "checkout", "v1.2.3"))
},
Assert: func(t *testing.T, ref string, err error) {
require.NoError(t, err)
require.Equal(t, "refs/tags/v1.2.3", ref)
},
},
"current_head_is_same_as_tag": {
Prepare: func(t *testing.T, dir string) {
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "1.4.2 release"))
require.NoError(t, gitCmd("-C", dir, "tag", "v1.4.2"))
},
Assert: func(t *testing.T, ref string, err error) {
require.NoError(t, err)
require.Equal(t, "refs/tags/v1.4.2", ref)
},
},
"current_head_is_not_tag": {
Prepare: func(t *testing.T, dir string) {
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "msg"))
require.NoError(t, gitCmd("-C", dir, "tag", "v1.4.2"))
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "msg2"))
},
Assert: func(t *testing.T, ref string, err error) {
require.NoError(t, err)
require.Equal(t, "refs/heads/master", ref)
},
},
"current_head_is_another_branch": {
Prepare: func(t *testing.T, dir string) {
require.NoError(t, gitCmd("-C", dir, "checkout", "-b", "mybranch"))
require.NoError(t, gitCmd("-C", dir, "commit", "--allow-empty", "-m", "msg"))
},
Assert: func(t *testing.T, ref string, err error) {
require.NoError(t, err)
require.Equal(t, "refs/heads/mybranch", ref)
},
},
} {
t.Run(name, func(t *testing.T) {
dir := filepath.Join(basedir, name)
require.NoError(t, os.MkdirAll(dir, 0o755))
require.NoError(t, gitCmd("-C", dir, "init", "--initial-branch=master"))
require.NoError(t, cleanGitHooks(dir))
tt.Prepare(t, dir)
ref, err := FindGitRef(context.Background(), dir)
tt.Assert(t, ref, err)
})
}
}
func TestGitCloneExecutor(t *testing.T) {
// Build a local bare "remote" so this runs offline and fast. The cases below mirror
// the tag/branch/sha/short-sha ref paths the executor handles, formerly exercised by
// cloning actions/checkout and anchore/scan-action over the network.
remoteDir := t.TempDir()
require.NoError(t, gitCmd("init", "--bare", "--initial-branch=main", remoteDir))
workDir := t.TempDir()
require.NoError(t, gitCmd("clone", remoteDir, workDir))
require.NoError(t, gitCmd("-C", workDir, "checkout", "-b", "main"))
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "initial"))
require.NoError(t, gitCmd("-C", workDir, "tag", "v2"))
require.NoError(t, gitCmd("-C", workDir, "push", "-u", "origin", "main"))
require.NoError(t, gitCmd("-C", workDir, "push", "origin", "v2"))
// A branch with a dash in the name (mirrors the historical scan-action@act-fails case).
require.NoError(t, gitCmd("-C", workDir, "checkout", "-b", "act-fails"))
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "branch-commit"))
require.NoError(t, gitCmd("-C", workDir, "push", "origin", "act-fails"))
out, err := exec.Command("git", "-C", workDir, "rev-parse", "main").Output()
require.NoError(t, err)
fullSha := strings.TrimSpace(string(out))
for name, tt := range map[string]struct {
Err error
Ref string
}{
"tag": {
Err: nil,
Ref: "v2",
},
"branch": {
Err: nil,
Ref: "act-fails",
},
"sha": {
Err: nil,
Ref: fullSha,
},
"short-sha": {
Err: &Error{ErrShortRef, fullSha},
Ref: fullSha[:7],
},
} {
t.Run(name, func(t *testing.T) {
clone := NewGitCloneExecutor(NewGitCloneExecutorInput{
URL: remoteDir,
Ref: tt.Ref,
Dir: t.TempDir(),
})
err := clone(context.Background())
if tt.Err != nil {
assert.Error(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, tt.Err, err)
} else {
assert.Empty(t, err) //nolint:testifylint // pre-existing issue from nektos/act
}
})
}
}
func TestGitCloneExecutorNonFastForwardRef(t *testing.T) {
// Simulate the scenario where a remote ref (e.g. a GitHub PR head ref) changes
// non-fast-forward between two fetches. Before the fix, the fetch used Force=false,
// causing go-git to return ErrForceNeeded and short-circuit the checkout.
// Create a bare "remote" repo with an initial commit on main and a feature branch.
remoteDir := t.TempDir()
require.NoError(t, gitCmd("init", "--bare", "--initial-branch=main", remoteDir))
// We need a working clone to push commits from.
workDir := t.TempDir()
require.NoError(t, gitCmd("clone", remoteDir, workDir))
require.NoError(t, gitCmd("-C", workDir, "checkout", "-b", "main"))
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "initial"))
require.NoError(t, gitCmd("-C", workDir, "push", "-u", "origin", "main"))
// Create a feature branch (simulates refs/pull/N/head).
require.NoError(t, gitCmd("-C", workDir, "checkout", "-b", "feature"))
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "feature-1"))
require.NoError(t, gitCmd("-C", workDir, "push", "origin", "feature"))
// First clone via the executor — should succeed and cache the repo.
cloneDir := t.TempDir()
clone := NewGitCloneExecutor(NewGitCloneExecutorInput{
URL: remoteDir,
Ref: "main",
Dir: cloneDir,
})
require.NoError(t, clone(context.Background()))
// Now force-push the feature branch to a non-fast-forward commit (simulates
// a PR rebase). This makes refs/heads/feature non-fast-forward.
require.NoError(t, gitCmd("-C", workDir, "checkout", "main"))
require.NoError(t, gitCmd("-C", workDir, "branch", "-D", "feature"))
require.NoError(t, gitCmd("-C", workDir, "checkout", "-b", "feature"))
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "feature-rewritten"))
require.NoError(t, gitCmd("-C", workDir, "push", "--force", "origin", "feature"))
// Also advance main so we can verify the clone picks up the new commit.
require.NoError(t, gitCmd("-C", workDir, "checkout", "main"))
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "second"))
require.NoError(t, gitCmd("-C", workDir, "push", "origin", "main"))
// Second clone to the same directory — before the fix this returned ErrForceNeeded
// and left the working tree at the old commit.
err := clone(context.Background())
require.NoError(t, err, "fetch with non-fast-forward refs must not fail when Force=true")
// Verify the working tree was actually updated to the latest main commit.
out, err := exec.Command("git", "-C", cloneDir, "log", "--oneline", "-1", "--format=%s").Output()
require.NoError(t, err)
assert.Equal(t, "second", strings.TrimSpace(string(out)), "working tree should be at the latest commit")
}
func TestGitCloneExecutorOfflineMode(t *testing.T) {
// Build a local "remote" with a single commit on main.
remoteDir := t.TempDir()
require.NoError(t, gitCmd("init", "--bare", "--initial-branch=main", remoteDir))
workDir := t.TempDir()
require.NoError(t, gitCmd("clone", remoteDir, workDir))
require.NoError(t, gitCmd("-C", workDir, "checkout", "-b", "main"))
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "initial"))
require.NoError(t, gitCmd("-C", workDir, "push", "-u", "origin", "main"))
// Prime the cache with an online clone of main.
cacheDir := t.TempDir()
require.NoError(t, NewGitCloneExecutor(NewGitCloneExecutorInput{
URL: remoteDir,
Ref: "main",
Dir: cacheDir,
})(context.Background()))
t.Run("cached branch resolves without fetching", func(t *testing.T) {
// Offline reuse of a cached branch must succeed even though ResolveRevision(input.Ref)
// finds no local refs/heads/<ref>.
err := NewGitCloneExecutor(NewGitCloneExecutorInput{
URL: remoteDir,
Ref: "main",
Dir: cacheDir,
OfflineMode: true,
})(context.Background())
require.NoError(t, err)
out, err := exec.Command("git", "-C", cacheDir, "log", "--oneline", "-1", "--format=%s").Output()
require.NoError(t, err)
assert.Equal(t, "initial", strings.TrimSpace(string(out)))
})
t.Run("unresolvable cached ref returns error", func(t *testing.T) {
// The ref was never cached; offline mode cannot resolve it and must return an error.
err := NewGitCloneExecutor(NewGitCloneExecutorInput{
URL: remoteDir,
Ref: "never-fetched",
Dir: cacheDir,
OfflineMode: true,
})(context.Background())
require.Error(t, err)
})
}
func gitCmd(args ...string) error {
cmd := exec.Command("git", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
// Inject a deterministic identity and ignore the host's global/system config so commits
// succeed regardless of the host having no user.name/user.email (e.g. CI, GITHUB_ACTIONS
// unset) or a global commit.gpgsign, and without mutating the developer's ~/.gitconfig.
cmd.Env = append(os.Environ(),
"GIT_AUTHOR_NAME=Unit Test",
"GIT_AUTHOR_EMAIL=test@test.com",
"GIT_COMMITTER_NAME=Unit Test",
"GIT_COMMITTER_EMAIL=test@test.com",
"GIT_CONFIG_GLOBAL=/dev/null",
"GIT_CONFIG_SYSTEM=/dev/null",
)
err := cmd.Run()
if exitError, ok := err.(*exec.ExitError); ok {
if waitStatus, ok := exitError.Sys().(syscall.WaitStatus); ok {
return fmt.Errorf("Exit error %d", waitStatus.ExitStatus())
}
return exitError
}
return nil
}
func TestAcquireCloneLock(t *testing.T) {
t.Run("same directory serializes", func(t *testing.T) {
dir := t.TempDir()
unlock1 := AcquireCloneLock(dir)
secondAcquired := make(chan struct{})
go func() {
unlock := AcquireCloneLock(dir)
close(secondAcquired)
unlock()
}()
select {
case <-secondAcquired:
t.Fatal("second acquire should block while first holds the lock")
case <-time.After(50 * time.Millisecond):
}
unlock1()
select {
case <-secondAcquired:
case <-time.After(time.Second):
t.Fatal("second acquire should proceed after first releases the lock")
}
})
t.Run("different directories do not block", func(t *testing.T) {
dirA := t.TempDir()
dirB := t.TempDir()
unlockA := AcquireCloneLock(dirA)
defer unlockA()
done := make(chan struct{})
go func() {
unlock := AcquireCloneLock(dirB)
unlock()
close(done)
}()
select {
case <-done:
case <-time.After(time.Second):
t.Fatal("acquire on a different directory must not block")
}
})
t.Run("same directory reuses the same mutex", func(t *testing.T) {
dir := t.TempDir()
v1, _ := cloneLocks.LoadOrStore(dir, &sync.Mutex{})
v2, _ := cloneLocks.LoadOrStore(dir, &sync.Mutex{})
require.Same(t, v1, v2)
})
}

View File

@@ -1,3 +1,7 @@
// Copyright 2021 The Gitea Authors. All rights reserved.
// Copyright 2021 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (

View File

@@ -1,3 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (

View File

@@ -1,3 +1,7 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (
@@ -18,7 +22,7 @@ func TestLineWriter(t *testing.T) {
assert := assert.New(t)
write := func(s string) {
n, err := lineWriter.Write([]byte(s))
assert.NoError(err)
assert.NoError(err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(len(s), n, s)
}

View File

@@ -1,3 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (

View File

@@ -1,3 +1,7 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2021 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package common
import (

View File

@@ -1,37 +1,53 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2023 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
"context"
"fmt"
"io"
"github.com/nektos/act/pkg/common"
"gitea.com/gitea/runner/act/common"
"github.com/docker/go-connections/nat"
)
// ExitCodeError reports a non-zero process exit code from a container command.
type ExitCodeError int
func (e ExitCodeError) Error() string {
return fmt.Sprintf("Process completed with exit code %d.", int(e))
}
// NewContainerInput the input for the New function
type NewContainerInput struct {
Image string
Username string
Password string
Entrypoint []string
Cmd []string
WorkingDir string
Env []string
Binds []string
Mounts map[string]string
Name string
Stdout io.Writer
Stderr io.Writer
NetworkMode string
Privileged bool
UsernsMode string
Platform string
Options string
Image string
Username string
Password string
Entrypoint []string
Cmd []string
WorkingDir string
Env []string
Binds []string
Mounts map[string]string
Name string
Stdout io.Writer
Stderr io.Writer
NetworkMode string
Privileged bool
UsernsMode string
Platform string
Options string
NetworkAliases []string
ExposedPorts nat.PortSet
PortBindings nat.PortMap
// Gitea specific
AutoRemove bool
NetworkAliases []string
ValidVolumes []string
AutoRemove bool
ValidVolumes []string
AllocatePTY bool // allocate a pseudo-TTY for the container's exec processes
}
// FileEntry is a file to copy to a container
@@ -43,11 +59,11 @@ type FileEntry struct {
// Container for managing docker run containers
type Container interface {
Create(capAdd []string, capDrop []string) common.Executor
Create(capAdd, capDrop []string) common.Executor
ConnectToNetwork(name string) common.Executor
Copy(destPath string, files ...*FileEntry) common.Executor
CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error
CopyDir(destPath string, srcPath string, useGitIgnore bool) common.Executor
CopyDir(destPath, srcPath string, useGitIgnore bool) common.Executor
GetContainerArchive(ctx context.Context, srcPath string) (io.ReadCloser, error)
Pull(forcePull bool) common.Executor
Start(attach bool) common.Executor

View File

@@ -0,0 +1,71 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2021 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd))
package container
import (
"context"
"gitea.com/gitea/runner/act/common"
"github.com/distribution/reference"
"github.com/docker/cli/cli/config"
"github.com/moby/moby/api/types/registry"
)
func LoadDockerAuthConfig(ctx context.Context, image string) (registry.AuthConfig, error) {
logger := common.Logger(ctx)
// config.LoadDefaultConfigFile panics on nil io.Writer when the config
// file is malformed; use config.Load to route errors through the logger.
cfg, err := config.Load(config.Dir())
if err != nil {
logger.Warnf("Could not load docker config: %v", err)
return registry.AuthConfig{}, err
}
registryKey := registryAuthConfigKey("docker.io")
if image != "" {
if registryRef, refErr := reference.ParseNormalizedNamed(image); refErr != nil {
logger.Warnf("Could not normalize image reference: %v", refErr)
} else {
registryKey = registryAuthConfigKey(reference.Domain(registryRef))
}
}
authConfig, err := cfg.GetAuthConfig(registryKey)
if err != nil {
logger.Warnf("Could not get auth config from docker config: %v", err)
return registry.AuthConfig{}, err
}
return registry.AuthConfig(authConfig), nil
}
func LoadDockerAuthConfigs(ctx context.Context) map[string]registry.AuthConfig {
logger := common.Logger(ctx)
cfg, err := config.Load(config.Dir())
if err != nil {
logger.Warnf("Could not load docker config: %v", err)
return nil
}
creds, err := cfg.GetAllCredentials()
if err != nil {
logger.Warnf("Could not get docker auth configs: %v", err)
return nil
}
authConfigs := make(map[string]registry.AuthConfig, len(creds))
for k, v := range creds {
authConfigs[k] = registry.AuthConfig(v)
}
return authConfigs
}
func registryAuthConfigKey(domainName string) string {
if domainName == "docker.io" || domainName == "index.docker.io" {
return "https://index.docker.io/v1/"
}
return domainName
}

View File

@@ -1,4 +1,8 @@
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows))
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd))
package container
@@ -8,14 +12,14 @@ import (
"os"
"path/filepath"
"github.com/docker/docker/api/types"
"github.com/docker/docker/pkg/archive"
"gitea.com/gitea/runner/act/common"
// github.com/docker/docker/builder/dockerignore is deprecated
"github.com/moby/buildkit/frontend/dockerfile/dockerignore"
"github.com/moby/go-archive"
"github.com/moby/go-archive/compression"
"github.com/moby/moby/client"
"github.com/moby/patternmatcher"
"github.com/nektos/act/pkg/common"
"github.com/moby/patternmatcher/ignorefile"
specs "github.com/opencontainers/image-spec/specs-go/v1"
)
// NewDockerBuildExecutor function to create a run executor for the container
@@ -23,9 +27,9 @@ func NewDockerBuildExecutor(input NewDockerBuildExecutorInput) common.Executor {
return func(ctx context.Context) error {
logger := common.Logger(ctx)
if input.Platform != "" {
logger.Infof("%sdocker build -t %s --platform %s %s", logPrefix, input.ImageTag, input.Platform, input.ContextDir)
logger.Infof("docker build -t %s --platform %s %s", input.ImageTag, input.Platform, input.ContextDir)
} else {
logger.Infof("%sdocker build -t %s %s", logPrefix, input.ImageTag, input.ContextDir)
logger.Infof("docker build -t %s %s", input.ImageTag, input.ContextDir)
}
if common.Dryrun(ctx) {
return nil
@@ -40,13 +44,19 @@ func NewDockerBuildExecutor(input NewDockerBuildExecutorInput) common.Executor {
logger.Debugf("Building image from '%v'", input.ContextDir)
tags := []string{input.ImageTag}
options := types.ImageBuildOptions{
options := client.ImageBuildOptions{
Tags: tags,
Remove: true,
Platform: input.Platform,
AuthConfigs: LoadDockerAuthConfigs(ctx),
Dockerfile: input.Dockerfile,
}
platform, err := parsePlatform(input.Platform)
if err != nil {
return err
}
if platform != nil {
options.Platforms = []specs.Platform{*platform}
}
var buildContext io.ReadCloser
if input.BuildContext != nil {
buildContext = io.NopCloser(input.BuildContext)
@@ -69,11 +79,12 @@ func NewDockerBuildExecutor(input NewDockerBuildExecutorInput) common.Executor {
return nil
}
}
func createBuildContext(ctx context.Context, contextDir string, relDockerfile string) (io.ReadCloser, error) {
func createBuildContext(ctx context.Context, contextDir, relDockerfile string) (io.ReadCloser, error) {
common.Logger(ctx).Debugf("Creating archive for build context dir '%s' with relative dockerfile '%s'", contextDir, relDockerfile)
// And canonicalize dockerfile name to a platform-independent one
relDockerfile = archive.CanonicalTarNameForPath(relDockerfile)
relDockerfile = filepath.ToSlash(relDockerfile)
f, err := os.Open(filepath.Join(contextDir, ".dockerignore"))
if err != nil && !os.IsNotExist(err) {
@@ -83,7 +94,7 @@ func createBuildContext(ctx context.Context, contextDir string, relDockerfile st
var excludes []string
if err == nil {
excludes, err = dockerignore.ReadAll(f)
excludes, err = ignorefile.ReadAll(f)
if err != nil {
return nil, err
}
@@ -96,16 +107,15 @@ func createBuildContext(ctx context.Context, contextDir string, relDockerfile st
// removed. The daemon will remove them for us, if needed, after it
// parses the Dockerfile. Ignore errors here, as they will have been
// caught by validateContextDirectory above.
var includes = []string{"."}
includes := []string{"."}
keepThem1, _ := patternmatcher.Matches(".dockerignore", excludes)
keepThem2, _ := patternmatcher.Matches(relDockerfile, excludes)
if keepThem1 || keepThem2 {
includes = append(includes, ".dockerignore", relDockerfile)
}
compression := archive.Uncompressed
buildCtx, err := archive.TarWithOptions(contextDir, &archive.TarOptions{
Compression: compression,
Compression: compression.None,
ExcludePatterns: excludes,
IncludeFiles: includes,
})

File diff suppressed because it is too large Load Diff

View File

@@ -1,3 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2022 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
// This file is exact copy of https://github.com/docker/cli/blob/9ac8584acfd501c3f4da0e845e3a40ed15c85041/cli/command/container/opts_test.go with:
// * appended with license information
// * commented out case 'invalid-mixed-network-types' in test TestParseNetworkConfig
@@ -6,21 +10,24 @@
// See DOCKER_LICENSE for the full license text.
//
//nolint:unparam,whitespace,depguard,dupl,gocritic
//nolint:depguard,gocritic // verbatim copy from docker/cli tests
package container
import (
"fmt"
"io"
"net/netip"
"os"
"runtime"
"strings"
"testing"
"time"
"github.com/docker/docker/api/types/container"
networktypes "github.com/docker/docker/api/types/network"
"github.com/docker/go-connections/nat"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
"github.com/moby/moby/api/types/container"
networktypes "github.com/moby/moby/api/types/network"
"github.com/pkg/errors"
"github.com/spf13/pflag"
"gotest.tools/v3/assert"
@@ -73,21 +80,21 @@ func setupRunFlags() (*pflag.FlagSet, *containerOptions) {
return flags, copts
}
func mustParse(t *testing.T, args string) (*container.Config, *container.HostConfig) {
func mustParse(t *testing.T, args string) (*container.Config, *container.HostConfig, *networktypes.NetworkingConfig) {
t.Helper()
config, hostConfig, _, err := parseRun(append(strings.Split(args, " "), "ubuntu", "bash"))
config, hostConfig, networkingConfig, err := parseRun(append(strings.Split(args, " "), "ubuntu", "bash"))
assert.NilError(t, err)
return config, hostConfig
return config, hostConfig, networkingConfig
}
func TestParseRunLinks(t *testing.T) {
if _, hostConfig := mustParse(t, "--link a:b"); len(hostConfig.Links) == 0 || hostConfig.Links[0] != "a:b" {
if _, hostConfig, _ := mustParse(t, "--link a:b"); len(hostConfig.Links) == 0 || hostConfig.Links[0] != "a:b" {
t.Fatalf("Error parsing links. Expected []string{\"a:b\"}, received: %v", hostConfig.Links)
}
if _, hostConfig := mustParse(t, "--link a:b --link c:d"); len(hostConfig.Links) < 2 || hostConfig.Links[0] != "a:b" || hostConfig.Links[1] != "c:d" {
if _, hostConfig, _ := mustParse(t, "--link a:b --link c:d"); len(hostConfig.Links) < 2 || hostConfig.Links[0] != "a:b" || hostConfig.Links[1] != "c:d" {
t.Fatalf("Error parsing links. Expected []string{\"a:b\", \"c:d\"}, received: %v", hostConfig.Links)
}
if _, hostConfig := mustParse(t, ""); len(hostConfig.Links) != 0 {
if _, hostConfig, _ := mustParse(t, ""); len(hostConfig.Links) != 0 {
t.Fatalf("Error parsing links. No link expected, received: %v", hostConfig.Links)
}
}
@@ -135,9 +142,8 @@ func TestParseRunAttach(t *testing.T) {
},
}
for _, tc := range tests {
tc := tc
t.Run(tc.input, func(t *testing.T) {
config, _ := mustParse(t, tc.input)
config, _, _ := mustParse(t, tc.input)
assert.Equal(t, config.AttachStdin, tc.expected.AttachStdin)
assert.Equal(t, config.AttachStdout, tc.expected.AttachStdout)
assert.Equal(t, config.AttachStderr, tc.expected.AttachStderr)
@@ -191,12 +197,10 @@ func TestParseRunWithInvalidArgs(t *testing.T) {
}
}
//nolint:gocyclo
func TestParseWithVolumes(t *testing.T) {
func TestParseWithVolumes(t *testing.T) { //nolint:gocyclo // verbatim copy from docker/cli tests
// A single volume
arr, tryit := setupPlatformVolume([]string{`/tmp`}, []string{`c:\tmp`})
if config, hostConfig := mustParse(t, tryit); hostConfig.Binds != nil {
if config, hostConfig, _ := mustParse(t, tryit); hostConfig.Binds != nil {
t.Fatalf("Error parsing volume flags, %q should not mount-bind anything. Received %v", tryit, hostConfig.Binds)
} else if _, exists := config.Volumes[arr[0]]; !exists {
t.Fatalf("Error parsing volume flags, %q is missing from volumes. Received %v", tryit, config.Volumes)
@@ -204,7 +208,7 @@ func TestParseWithVolumes(t *testing.T) {
// Two volumes
arr, tryit = setupPlatformVolume([]string{`/tmp`, `/var`}, []string{`c:\tmp`, `c:\var`})
if config, hostConfig := mustParse(t, tryit); hostConfig.Binds != nil {
if config, hostConfig, _ := mustParse(t, tryit); hostConfig.Binds != nil {
t.Fatalf("Error parsing volume flags, %q should not mount-bind anything. Received %v", tryit, hostConfig.Binds)
} else if _, exists := config.Volumes[arr[0]]; !exists {
t.Fatalf("Error parsing volume flags, %s is missing from volumes. Received %v", arr[0], config.Volumes)
@@ -214,13 +218,13 @@ func TestParseWithVolumes(t *testing.T) {
// A single bind mount
arr, tryit = setupPlatformVolume([]string{`/hostTmp:/containerTmp`}, []string{os.Getenv("TEMP") + `:c:\containerTmp`})
if config, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || hostConfig.Binds[0] != arr[0] {
if config, hostConfig, _ := mustParse(t, tryit); hostConfig.Binds == nil || hostConfig.Binds[0] != arr[0] {
t.Fatalf("Error parsing volume flags, %q should mount-bind the path before the colon into the path after the colon. Received %v %v", arr[0], hostConfig.Binds, config.Volumes)
}
// Two bind mounts.
arr, tryit = setupPlatformVolume([]string{`/hostTmp:/containerTmp`, `/hostVar:/containerVar`}, []string{os.Getenv("ProgramData") + `:c:\ContainerPD`, os.Getenv("TEMP") + `:c:\containerTmp`})
if _, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
if _, hostConfig, _ := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
t.Fatalf("Error parsing volume flags, `%s and %s` did not mount-bind correctly. Received %v", arr[0], arr[1], hostConfig.Binds)
}
@@ -229,26 +233,26 @@ func TestParseWithVolumes(t *testing.T) {
arr, tryit = setupPlatformVolume(
[]string{`/hostTmp:/containerTmp:ro`, `/hostVar:/containerVar:rw`},
[]string{os.Getenv("TEMP") + `:c:\containerTmp:rw`, os.Getenv("ProgramData") + `:c:\ContainerPD:rw`})
if _, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
if _, hostConfig, _ := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
t.Fatalf("Error parsing volume flags, `%s and %s` did not mount-bind correctly. Received %v", arr[0], arr[1], hostConfig.Binds)
}
// Similar to previous test but with alternate modes which are only supported by Linux
if runtime.GOOS != "windows" {
arr, tryit = setupPlatformVolume([]string{`/hostTmp:/containerTmp:ro,Z`, `/hostVar:/containerVar:rw,Z`}, []string{})
if _, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
if _, hostConfig, _ := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
t.Fatalf("Error parsing volume flags, `%s and %s` did not mount-bind correctly. Received %v", arr[0], arr[1], hostConfig.Binds)
}
arr, tryit = setupPlatformVolume([]string{`/hostTmp:/containerTmp:Z`, `/hostVar:/containerVar:z`}, []string{})
if _, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
if _, hostConfig, _ := mustParse(t, tryit); hostConfig.Binds == nil || compareRandomizedStrings(hostConfig.Binds[0], hostConfig.Binds[1], arr[0], arr[1]) != nil {
t.Fatalf("Error parsing volume flags, `%s and %s` did not mount-bind correctly. Received %v", arr[0], arr[1], hostConfig.Binds)
}
}
// One bind mount and one volume
arr, tryit = setupPlatformVolume([]string{`/hostTmp:/containerTmp`, `/containerVar`}, []string{os.Getenv("TEMP") + `:c:\containerTmp`, `c:\containerTmp`})
if config, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != arr[0] {
if config, hostConfig, _ := mustParse(t, tryit); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != arr[0] {
t.Fatalf("Error parsing volume flags, %s and %s should only one and only one bind mount %s. Received %s", arr[0], arr[1], arr[0], hostConfig.Binds)
} else if _, exists := config.Volumes[arr[1]]; !exists {
t.Fatalf("Error parsing volume flags %s and %s. %s is missing from volumes. Received %v", arr[0], arr[1], arr[1], config.Volumes)
@@ -257,18 +261,17 @@ func TestParseWithVolumes(t *testing.T) {
// Root to non-c: drive letter (Windows specific)
if runtime.GOOS == "windows" {
arr, tryit = setupPlatformVolume([]string{}, []string{os.Getenv("SystemDrive") + `\:d:`})
if config, hostConfig := mustParse(t, tryit); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != arr[0] || len(config.Volumes) != 0 {
if config, hostConfig, _ := mustParse(t, tryit); hostConfig.Binds == nil || len(hostConfig.Binds) > 1 || hostConfig.Binds[0] != arr[0] || len(config.Volumes) != 0 {
t.Fatalf("Error parsing %s. Should have a single bind mount and no volumes", arr[0])
}
}
}
// setupPlatformVolume takes two arrays of volume specs - a Unix style
// spec and a Windows style spec. Depending on the platform being unit tested,
// it returns one of them, along with a volume string that would be passed
// on the docker CLI (e.g. -v /bar -v /foo).
func setupPlatformVolume(u []string, w []string) ([]string, string) {
func setupPlatformVolume(u, w []string) ([]string, string) {
var a []string
if runtime.GOOS == "windows" {
a = w
@@ -294,6 +297,36 @@ func compareRandomizedStrings(a, b, c, d string) error {
return errors.Errorf("strings don't match")
}
func mustNetworkPort(t *testing.T, value string) networktypes.Port {
t.Helper()
port, err := networktypes.ParsePort(value)
if err != nil {
t.Fatalf("failed to parse network port %q: %v", value, err)
}
return port
}
func mustAddr(t *testing.T, value string) netip.Addr {
t.Helper()
addr, err := netip.ParseAddr(value)
if err != nil {
t.Fatalf("failed to parse address %q: %v", value, err)
}
return addr
}
func mustAddrs(t *testing.T, values ...string) []netip.Addr {
t.Helper()
addrs := make([]netip.Addr, 0, len(values))
for _, value := range values {
addrs = append(addrs, mustAddr(t, value))
}
return addrs
}
// Simple parse with MacAddress validation
func TestParseWithMacAddress(t *testing.T) {
invalidMacAddress := "--mac-address=invalidMacAddress"
@@ -301,9 +334,10 @@ func TestParseWithMacAddress(t *testing.T) {
if _, _, _, err := parseRun([]string{invalidMacAddress, "img", "cmd"}); err != nil && err.Error() != "invalidMacAddress is not a valid mac address" {
t.Fatalf("Expected an error with %v mac-address, got %v", invalidMacAddress, err)
}
if config, _ := mustParse(t, validMacAddress); config.MacAddress != "92:d0:c6:0a:29:33" {
t.Fatalf("Expected the config to have '92:d0:c6:0a:29:33' as MacAddress, got '%v'", config.MacAddress)
}
_, hostConfig, networkingConfig := mustParse(t, validMacAddress)
endpoint := networkingConfig.EndpointsConfig[string(hostConfig.NetworkMode)]
assert.Check(t, endpoint != nil)
assert.Equal(t, "92:d0:c6:0a:29:33", endpoint.MacAddress.String())
}
func TestRunFlagsParseWithMemory(t *testing.T) {
@@ -312,7 +346,7 @@ func TestRunFlagsParseWithMemory(t *testing.T) {
err := flags.Parse(args)
assert.ErrorContains(t, err, `invalid argument "invalid" for "-m, --memory" flag`)
_, hostconfig := mustParse(t, "--memory=1G")
_, hostconfig, _ := mustParse(t, "--memory=1G")
assert.Check(t, is.Equal(int64(1073741824), hostconfig.Memory))
}
@@ -322,10 +356,10 @@ func TestParseWithMemorySwap(t *testing.T) {
err := flags.Parse(args)
assert.ErrorContains(t, err, `invalid argument "invalid" for "--memory-swap" flag`)
_, hostconfig := mustParse(t, "--memory-swap=1G")
_, hostconfig, _ := mustParse(t, "--memory-swap=1G")
assert.Check(t, is.Equal(int64(1073741824), hostconfig.MemorySwap))
_, hostconfig = mustParse(t, "--memory-swap=-1")
_, hostconfig, _ = mustParse(t, "--memory-swap=-1")
assert.Check(t, is.Equal(int64(-1), hostconfig.MemorySwap))
}
@@ -340,14 +374,14 @@ func TestParseHostname(t *testing.T) {
hostnameWithDomain := "--hostname=hostname.domainname"
hostnameWithDomainTld := "--hostname=hostname.domainname.tld"
for hostname, expectedHostname := range validHostnames {
if config, _ := mustParse(t, fmt.Sprintf("--hostname=%s", hostname)); config.Hostname != expectedHostname {
if config, _, _ := mustParse(t, "--hostname="+hostname); config.Hostname != expectedHostname {
t.Fatalf("Expected the config to have 'hostname' as %q, got %q", expectedHostname, config.Hostname)
}
}
if config, _ := mustParse(t, hostnameWithDomain); config.Hostname != "hostname.domainname" || config.Domainname != "" {
if config, _, _ := mustParse(t, hostnameWithDomain); config.Hostname != "hostname.domainname" || config.Domainname != "" {
t.Fatalf("Expected the config to have 'hostname' as hostname.domainname, got %q", config.Hostname)
}
if config, _ := mustParse(t, hostnameWithDomainTld); config.Hostname != "hostname.domainname.tld" || config.Domainname != "" {
if config, _, _ := mustParse(t, hostnameWithDomainTld); config.Hostname != "hostname.domainname.tld" || config.Domainname != "" {
t.Fatalf("Expected the config to have 'hostname' as hostname.domainname.tld, got %q", config.Hostname)
}
}
@@ -361,28 +395,28 @@ func TestParseHostnameDomainname(t *testing.T) {
"domainname-63-bytes-long-should-be-valid-and-without-any-errors": "domainname-63-bytes-long-should-be-valid-and-without-any-errors",
}
for domainname, expectedDomainname := range validDomainnames {
if config, _ := mustParse(t, "--domainname="+domainname); config.Domainname != expectedDomainname {
if config, _, _ := mustParse(t, "--domainname="+domainname); config.Domainname != expectedDomainname {
t.Fatalf("Expected the config to have 'domainname' as %q, got %q", expectedDomainname, config.Domainname)
}
}
if config, _ := mustParse(t, "--hostname=some.prefix --domainname=domainname"); config.Hostname != "some.prefix" || config.Domainname != "domainname" {
if config, _, _ := mustParse(t, "--hostname=some.prefix --domainname=domainname"); config.Hostname != "some.prefix" || config.Domainname != "domainname" {
t.Fatalf("Expected the config to have 'hostname' as 'some.prefix' and 'domainname' as 'domainname', got %q and %q", config.Hostname, config.Domainname)
}
if config, _ := mustParse(t, "--hostname=another-prefix --domainname=domainname.tld"); config.Hostname != "another-prefix" || config.Domainname != "domainname.tld" {
if config, _, _ := mustParse(t, "--hostname=another-prefix --domainname=domainname.tld"); config.Hostname != "another-prefix" || config.Domainname != "domainname.tld" {
t.Fatalf("Expected the config to have 'hostname' as 'another-prefix' and 'domainname' as 'domainname.tld', got %q and %q", config.Hostname, config.Domainname)
}
}
func TestParseWithExpose(t *testing.T) {
invalids := map[string]string{
":": "invalid port format for --expose: :",
"8080:9090": "invalid port format for --expose: 8080:9090",
"/tcp": "invalid range format for --expose: /tcp, error: Empty string specified for ports.",
"/udp": "invalid range format for --expose: /udp, error: Empty string specified for ports.",
"NaN/tcp": `invalid range format for --expose: NaN/tcp, error: strconv.ParseUint: parsing "NaN": invalid syntax`,
"NaN-NaN/tcp": `invalid range format for --expose: NaN-NaN/tcp, error: strconv.ParseUint: parsing "NaN": invalid syntax`,
"8080-NaN/tcp": `invalid range format for --expose: 8080-NaN/tcp, error: strconv.ParseUint: parsing "NaN": invalid syntax`,
"1234567890-8080/tcp": `invalid range format for --expose: 1234567890-8080/tcp, error: strconv.ParseUint: parsing "1234567890": value out of range`,
invalids := []string{
":",
"8080:9090",
"/tcp",
"/udp",
"NaN/tcp",
"NaN-NaN/tcp",
"8080-NaN/tcp",
"1234567890-8080/tcp",
}
valids := map[string][]nat.Port{
"8080/tcp": {"8080/tcp"},
@@ -391,9 +425,9 @@ func TestParseWithExpose(t *testing.T) {
"8080-8080/udp": {"8080/udp"},
"8080-8082/tcp": {"8080/tcp", "8081/tcp", "8082/tcp"},
}
for expose, expectedError := range invalids {
if _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil || err.Error() != expectedError {
t.Fatalf("Expected error '%v' with '--expose=%v', got '%v'", expectedError, expose, err)
for _, expose := range invalids {
if _, _, _, err := parseRun([]string{fmt.Sprintf("--expose=%v", expose), "img", "cmd"}); err == nil {
t.Fatalf("Expected error with '--expose=%v', got none", expose)
}
}
for expose, exposedPorts := range valids {
@@ -405,7 +439,7 @@ func TestParseWithExpose(t *testing.T) {
t.Fatalf("Expected %v exposed port, got %v", len(exposedPorts), len(config.ExposedPorts))
}
for _, port := range exposedPorts {
if _, ok := config.ExposedPorts[port]; !ok {
if _, ok := config.ExposedPorts[mustNetworkPort(t, string(port))]; !ok {
t.Fatalf("Expected %v, got %v", exposedPorts, config.ExposedPorts)
}
}
@@ -420,7 +454,7 @@ func TestParseWithExpose(t *testing.T) {
}
ports := []nat.Port{"80/tcp", "81/tcp"}
for _, port := range ports {
if _, ok := config.ExposedPorts[port]; !ok {
if _, ok := config.ExposedPorts[mustNetworkPort(t, string(port))]; !ok {
t.Fatalf("Expected %v, got %v", ports, config.ExposedPorts)
}
}
@@ -462,7 +496,6 @@ func TestParseDevice(t *testing.T) {
t.Fatalf("Expected %v, got %v", deviceMapping, hostconfig.Devices)
}
}
}
func TestParseNetworkConfig(t *testing.T) {
@@ -501,9 +534,9 @@ func TestParseNetworkConfig(t *testing.T) {
expected: map[string]*networktypes.EndpointSettings{
"net1": {
IPAMConfig: &networktypes.EndpointIPAMConfig{
IPv4Address: "172.20.88.22",
IPv6Address: "2001:db8::8822",
LinkLocalIPs: []string{"169.254.2.2", "fe80::169:254:2:2"},
IPv4Address: mustAddr(t, "172.20.88.22"),
IPv6Address: mustAddr(t, "2001:db8::8822"),
LinkLocalIPs: mustAddrs(t, "169.254.2.2", "fe80::169:254:2:2"),
},
Links: []string{"foo:bar", "bar:baz"},
Aliases: []string{"web1", "web2"},
@@ -530,9 +563,9 @@ func TestParseNetworkConfig(t *testing.T) {
"net1": {
DriverOpts: map[string]string{"field1": "value1"},
IPAMConfig: &networktypes.EndpointIPAMConfig{
IPv4Address: "172.20.88.22",
IPv6Address: "2001:db8::8822",
LinkLocalIPs: []string{"169.254.2.2", "fe80::169:254:2:2"},
IPv4Address: mustAddr(t, "172.20.88.22"),
IPv6Address: mustAddr(t, "2001:db8::8822"),
LinkLocalIPs: mustAddrs(t, "169.254.2.2", "fe80::169:254:2:2"),
},
Links: []string{"foo:bar", "bar:baz"},
Aliases: []string{"web1", "web2"},
@@ -541,8 +574,8 @@ func TestParseNetworkConfig(t *testing.T) {
"net3": {
DriverOpts: map[string]string{"field3": "value3"},
IPAMConfig: &networktypes.EndpointIPAMConfig{
IPv4Address: "172.20.88.22",
IPv6Address: "2001:db8::8822",
IPv4Address: mustAddr(t, "172.20.88.22"),
IPv6Address: mustAddr(t, "2001:db8::8822"),
},
Aliases: []string{"web3"},
},
@@ -559,8 +592,8 @@ func TestParseNetworkConfig(t *testing.T) {
"field2": "value2",
},
IPAMConfig: &networktypes.EndpointIPAMConfig{
IPv4Address: "172.20.88.22",
IPv6Address: "2001:db8::8822",
IPv4Address: mustAddr(t, "172.20.88.22"),
IPv6Address: mustAddr(t, "2001:db8::8822"),
},
Aliases: []string{"web1", "web2"},
},
@@ -613,7 +646,9 @@ func TestParseNetworkConfig(t *testing.T) {
assert.NilError(t, err)
assert.DeepEqual(t, hConfig.NetworkMode, tc.expectedCfg.NetworkMode)
assert.DeepEqual(t, nwConfig.EndpointsConfig, tc.expected)
if diff := cmp.Diff(tc.expected, nwConfig.EndpointsConfig, cmpopts.EquateComparable(netip.Addr{})); diff != "" {
t.Fatalf("unexpected endpoints (-want +got):\n%s", diff)
}
})
}
}
@@ -634,7 +669,7 @@ func TestParseModes(t *testing.T) {
}
// uts ko
_, _, _, err = parseRun([]string{"--uts=container:", "img", "cmd"}) //nolint:dogsled
_, _, _, err = parseRun([]string{"--uts=container:", "img", "cmd"}) //nolint:dogsled // verbatim copy from docker/cli tests
assert.ErrorContains(t, err, "--uts: invalid UTS mode")
// uts ok
@@ -678,7 +713,7 @@ func TestParseRestartPolicy(t *testing.T) {
},
}
for restart, expectedError := range invalids {
if _, _, _, err := parseRun([]string{fmt.Sprintf("--restart=%s", restart), "img", "cmd"}); err == nil || err.Error() != expectedError {
if _, _, _, err := parseRun([]string{"--restart=" + restart, "img", "cmd"}); err == nil || err.Error() != expectedError {
t.Fatalf("Expected an error with message '%v' for %v, got %v", expectedError, restart, err)
}
}
@@ -694,10 +729,9 @@ func TestParseRestartPolicy(t *testing.T) {
}
func TestParseRestartPolicyAutoRemove(t *testing.T) {
expected := "Conflicting options: --restart and --rm"
_, _, _, err := parseRun([]string{"--rm", "--restart=always", "img", "cmd"}) //nolint:dogsled
if err == nil || err.Error() != expected {
t.Fatalf("Expected error %v, but got none", expected)
_, _, _, err := parseRun([]string{"--rm", "--restart=always", "img", "cmd"}) //nolint:dogsled // verbatim copy from docker/cli tests
if err == nil {
t.Fatal("Expected error for conflicting --restart and --rm, but got none")
}
}
@@ -755,7 +789,7 @@ func TestParseLoggingOpts(t *testing.T) {
}
}
func TestParseEnvfileVariables(t *testing.T) {
func TestParseEnvfileVariables(t *testing.T) { //nolint:dupl // verbatim copy from docker/cli tests
e := "open nonexistent: no such file or directory"
if runtime.GOOS == "windows" {
e = "open nonexistent: The system cannot find the file specified."
@@ -798,7 +832,7 @@ func TestParseEnvfileVariablesWithBOMUnicode(t *testing.T) {
}
// UTF16 with BOM
e := "contains invalid utf8 bytes at line"
e := "invalid env file"
if _, _, _, err := parseRun([]string{"--env-file=testdata/utf16.env", "img", "cmd"}); err == nil || !strings.Contains(err.Error(), e) {
t.Fatalf("Expected an error with message '%s', got %v", e, err)
}
@@ -808,7 +842,7 @@ func TestParseEnvfileVariablesWithBOMUnicode(t *testing.T) {
}
}
func TestParseLabelfileVariables(t *testing.T) {
func TestParseLabelfileVariables(t *testing.T) { //nolint:dupl // verbatim copy from docker/cli tests
e := "open nonexistent: no such file or directory"
if runtime.GOOS == "windows" {
e = "open nonexistent: The system cannot find the file specified."
@@ -967,7 +1001,6 @@ func TestConvertToStandardNotation(t *testing.T) {
for key, ports := range valid {
convertedPorts, err := convertToStandardNotation(ports)
if err != nil {
assert.NilError(t, err)
}

View File

@@ -1,4 +1,8 @@
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows))
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd))
package container
@@ -6,21 +10,21 @@ import (
"context"
"fmt"
"github.com/docker/docker/api/types"
"github.com/docker/docker/client"
cerrdefs "github.com/containerd/errdefs"
"github.com/moby/moby/client"
)
// ImageExistsLocally returns a boolean indicating if an image with the
// requested name, tag and architecture exists in the local docker image store
func ImageExistsLocally(ctx context.Context, imageName string, platform string) (bool, error) {
func ImageExistsLocally(ctx context.Context, imageName, platform string) (bool, error) {
cli, err := GetDockerClient(ctx)
if err != nil {
return false, err
}
defer cli.Close()
inspectImage, _, err := cli.ImageInspectWithRaw(ctx, imageName)
if client.IsErrNotFound(err) {
inspectImage, err := cli.ImageInspect(ctx, imageName)
if cerrdefs.IsNotFound(err) {
return false, nil
} else if err != nil {
return false, err
@@ -35,21 +39,21 @@ func ImageExistsLocally(ctx context.Context, imageName string, platform string)
// RemoveImage removes image from local store, the function is used to run different
// container image architectures
func RemoveImage(ctx context.Context, imageName string, force bool, pruneChildren bool) (bool, error) {
func RemoveImage(ctx context.Context, imageName string, force, pruneChildren bool) (bool, error) {
cli, err := GetDockerClient(ctx)
if err != nil {
return false, err
}
defer cli.Close()
inspectImage, _, err := cli.ImageInspectWithRaw(ctx, imageName)
if client.IsErrNotFound(err) {
inspectImage, err := cli.ImageInspect(ctx, imageName)
if cerrdefs.IsNotFound(err) {
return false, nil
} else if err != nil {
return false, err
}
if _, err = cli.ImageRemove(ctx, inspectImage.ID, types.ImageRemoveOptions{
if _, err = cli.ImageRemove(ctx, inspectImage.ID, client.ImageRemoveOptions{
Force: force,
PruneChildren: pruneChildren,
}); err != nil {

View File

@@ -0,0 +1,69 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"testing"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func init() {
log.SetLevel(log.DebugLevel)
}
// buildScratchImage builds a tiny empty image for the given platform locally (FROM scratch, no
// network or emulation since there is nothing to run) and returns its tag, removing it after
// the test.
func buildScratchImage(t *testing.T, platform string) string {
t.Helper()
tag := fmt.Sprintf("act-test-exists-%s:latest", strings.TrimPrefix(platform, "linux/"))
cmd := exec.Command("docker", "build", "--platform", platform, "-t", tag, "-")
cmd.Stdin = strings.NewReader("FROM scratch\nLABEL act-test=1\n")
// Force BuildKit: it records the requested architecture in the image config for a
// FROM-scratch build, whereas the classic builder ignores --platform and tags it with the
// host arch, which would break the per-platform existence assertions below.
cmd.Env = append(os.Environ(), "DOCKER_BUILDKIT=1")
out, err := cmd.CombinedOutput()
require.NoError(t, err, string(out))
t.Cleanup(func() { _ = exec.Command("docker", "rmi", "-f", tag).Run() })
return tag
}
func TestImageExistsLocally(t *testing.T) {
requireDocker(t)
ctx := context.Background()
// a non-existent image is reported absent
missing, err := ImageExistsLocally(ctx, "library/alpine:this-random-tag-will-never-exist", "linux/amd64")
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.False(t, missing)
// Build tiny images for two architectures locally so per-platform existence can be checked
// offline (formerly pulled node:24-bookworm-slim for amd64 and arm64 over the network).
amd64Ref := buildScratchImage(t, "linux/amd64")
arm64Ref := buildScratchImage(t, "linux/arm64")
amd64Exists, err := ImageExistsLocally(ctx, amd64Ref, "linux/amd64")
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.True(t, amd64Exists)
// a non-host architecture image is detected for its own architecture
arm64Exists, err := ImageExistsLocally(ctx, arm64Ref, "linux/arm64")
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.True(t, arm64Exists)
// a present image is reported absent for a different platform
wrongPlatform, err := ImageExistsLocally(ctx, amd64Ref, "linux/arm64")
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.False(t, wrongPlatform)
}

View File

@@ -1,4 +1,8 @@
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows))
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd))
package container
@@ -22,8 +26,6 @@ type dockerMessage struct {
Progress string `json:"progress"`
}
const logPrefix = " \U0001F433 "
func logDockerResponse(logger logrus.FieldLogger, dockerResponse io.ReadCloser, isError bool) error {
if dockerResponse == nil {
return nil
@@ -74,7 +76,7 @@ func logDockerResponse(logger logrus.FieldLogger, dockerResponse io.ReadCloser,
return nil
}
func writeLog(logger logrus.FieldLogger, isError bool, format string, args ...interface{}) {
func writeLog(logger logrus.FieldLogger, isError bool, format string, args ...any) {
if isError {
logger.Errorf(format, args...)
} else {

View File

@@ -0,0 +1,86 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2023 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd))
package container
import (
"context"
"gitea.com/gitea/runner/act/common"
"github.com/moby/moby/client"
)
func NewDockerNetworkCreateExecutor(name string) common.Executor {
return func(ctx context.Context) error {
cli, err := GetDockerClient(ctx)
if err != nil {
return err
}
defer cli.Close()
// Only create the network if it doesn't exist
networks, err := cli.NetworkList(ctx, client.NetworkListOptions{})
if err != nil {
return err
}
// For Gitea, reduce log noise
// common.Logger(ctx).Debugf("%v", networks)
for _, n := range networks.Items {
if n.Name == name {
common.Logger(ctx).Debugf("Network %v exists", name)
return nil
}
}
_, err = cli.NetworkCreate(ctx, name, client.NetworkCreateOptions{
Driver: "bridge",
Scope: "local",
})
if err != nil {
return err
}
return nil
}
}
func NewDockerNetworkRemoveExecutor(name string) common.Executor {
return func(ctx context.Context) error {
cli, err := GetDockerClient(ctx)
if err != nil {
return err
}
defer cli.Close()
// Make sure that all network of the specified name are removed
// cli.NetworkRemove refuses to remove a network if there are duplicates
networks, err := cli.NetworkList(ctx, client.NetworkListOptions{})
if err != nil {
return err
}
// For Gitea, reduce log noise
// common.Logger(ctx).Debugf("%v", networks)
for _, n := range networks.Items {
if n.Name == name {
result, err := cli.NetworkInspect(ctx, n.ID, client.NetworkInspectOptions{})
if err != nil {
return err
}
if len(result.Network.Containers) == 0 {
if _, err = cli.NetworkRemove(ctx, n.ID, client.NetworkRemoveOptions{}); err != nil {
common.Logger(ctx).Debugf("%v", err)
}
} else {
common.Logger(ctx).Debugf("Refusing to remove network %v because it still has active endpoints", name)
}
}
}
return err
}
}

View File

@@ -0,0 +1,39 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// Copyright 2025 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd))
package container
import (
"fmt"
"strings"
specs "github.com/opencontainers/image-spec/specs-go/v1"
)
// parsePlatform parses an "os/arch[/variant]" string into a Platform. An empty input
// returns (nil, nil), meaning "no platform constraint". A non-empty but malformed
// string is rejected explicitly so it cannot silently fall through to the daemon's
// default architecture.
func parsePlatform(platform string) (*specs.Platform, error) {
if platform == "" {
return nil, nil //nolint:nilnil // no platform constraint requested
}
parts := strings.Split(platform, "/")
if len(parts) < 2 || len(parts) > 3 || parts[0] == "" || parts[1] == "" || (len(parts) == 3 && parts[2] == "") {
return nil, fmt.Errorf("invalid platform %q: expected os/arch[/variant]", platform)
}
spec := &specs.Platform{
OS: strings.ToLower(parts[0]),
Architecture: strings.ToLower(parts[1]),
}
if len(parts) == 3 {
spec.Variant = strings.ToLower(parts[2])
}
return spec, nil
}

View File

@@ -0,0 +1,63 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestParsePlatform(t *testing.T) {
t.Run("empty input returns nil platform without error", func(t *testing.T) {
got, err := parsePlatform("")
require.NoError(t, err)
assert.Nil(t, got)
})
t.Run("os/arch", func(t *testing.T) {
got, err := parsePlatform("linux/amd64")
require.NoError(t, err)
require.NotNil(t, got)
assert.Equal(t, "linux", got.OS)
assert.Equal(t, "amd64", got.Architecture)
assert.Empty(t, got.Variant)
})
t.Run("os/arch/variant", func(t *testing.T) {
got, err := parsePlatform("linux/arm/v7")
require.NoError(t, err)
require.NotNil(t, got)
assert.Equal(t, "linux", got.OS)
assert.Equal(t, "arm", got.Architecture)
assert.Equal(t, "v7", got.Variant)
})
t.Run("input is lowercased", func(t *testing.T) {
got, err := parsePlatform("Linux/AMD64/V8")
require.NoError(t, err)
require.NotNil(t, got)
assert.Equal(t, "linux", got.OS)
assert.Equal(t, "amd64", got.Architecture)
assert.Equal(t, "v8", got.Variant)
})
for _, bad := range []string{
"amd64",
"linux",
"linux/",
"/amd64",
"/",
"//",
"linux/arm/",
"linux/arm/v7/extra",
} {
t.Run("rejects "+bad, func(t *testing.T) {
got, err := parsePlatform(bad)
require.Error(t, err)
assert.Nil(t, got)
})
}
}

View File

@@ -1,26 +1,30 @@
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows))
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd))
package container
import (
"context"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"github.com/docker/distribution/reference"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/registry"
"gitea.com/gitea/runner/act/common"
"github.com/nektos/act/pkg/common"
"github.com/distribution/reference"
"github.com/moby/moby/api/pkg/authconfig"
"github.com/moby/moby/api/types/registry"
"github.com/moby/moby/client"
specs "github.com/opencontainers/image-spec/specs-go/v1"
)
// NewDockerPullExecutor function to create a run executor for the container
func NewDockerPullExecutor(input NewDockerPullExecutorInput) common.Executor {
return func(ctx context.Context) error {
logger := common.Logger(ctx)
logger.Debugf("%sdocker pull %v", logPrefix, input.Image)
logger.Debugf("docker pull %v", input.Image)
if common.Dryrun(ctx) {
return nil
@@ -74,26 +78,29 @@ func NewDockerPullExecutor(input NewDockerPullExecutorInput) common.Executor {
}
}
func getImagePullOptions(ctx context.Context, input NewDockerPullExecutorInput) (types.ImagePullOptions, error) {
imagePullOptions := types.ImagePullOptions{
Platform: input.Platform,
func getImagePullOptions(ctx context.Context, input NewDockerPullExecutorInput) (client.ImagePullOptions, error) {
imagePullOptions := client.ImagePullOptions{}
platform, err := parsePlatform(input.Platform)
if err != nil {
return imagePullOptions, err
}
if platform != nil {
imagePullOptions.Platforms = []specs.Platform{*platform}
}
logger := common.Logger(ctx)
if input.Username != "" && input.Password != "" {
logger.Debugf("using authentication for docker pull")
authConfig := registry.AuthConfig{
encodedAuth, err := authconfig.Encode(registry.AuthConfig{
Username: input.Username,
Password: input.Password,
}
encodedJSON, err := json.Marshal(authConfig)
})
if err != nil {
return imagePullOptions, err
}
imagePullOptions.RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
imagePullOptions.RegistryAuth = encodedAuth
} else {
authConfig, err := LoadDockerAuthConfig(ctx, input.Image)
if err != nil {
@@ -104,19 +111,17 @@ func getImagePullOptions(ctx context.Context, input NewDockerPullExecutorInput)
}
logger.Info("using DockerAuthConfig authentication for docker pull")
encodedJSON, err := json.Marshal(authConfig)
imagePullOptions.RegistryAuth, err = authconfig.Encode(authConfig)
if err != nil {
return imagePullOptions, err
}
imagePullOptions.RegistryAuth = base64.URLEncoding.EncodeToString(encodedJSON)
}
return imagePullOptions, nil
}
func cleanImage(ctx context.Context, image string) string {
ref, err := reference.ParseAnyReference(image)
func cleanImage(ctx context.Context, imageName string) string {
ref, err := reference.ParseAnyReference(imageName)
if err != nil {
common.Logger(ctx).Error(err)
return ""

View File

@@ -1,3 +1,7 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
@@ -5,7 +9,6 @@ import (
"testing"
"github.com/docker/cli/cli/config"
log "github.com/sirupsen/logrus"
assert "github.com/stretchr/testify/assert"
)
@@ -37,18 +40,21 @@ func TestCleanImage(t *testing.T) {
func TestGetImagePullOptions(t *testing.T) {
ctx := context.Background()
orig := config.Dir()
t.Cleanup(func() { config.SetDir(orig) })
config.SetDir("/non-existent/docker")
options, err := getImagePullOptions(ctx, NewDockerPullExecutorInput{})
assert.Nil(t, err, "Failed to create ImagePullOptions")
assert.Equal(t, "", options.RegistryAuth, "RegistryAuth should be empty if no username or password is set")
assert.NoError(t, err, "Failed to create ImagePullOptions") //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, "", options.RegistryAuth, "RegistryAuth should be empty if no username or password is set") //nolint:testifylint // pre-existing issue from nektos/act
options, err = getImagePullOptions(ctx, NewDockerPullExecutorInput{
Image: "",
Username: "username",
Password: "password",
})
assert.Nil(t, err, "Failed to create ImagePullOptions")
assert.NoError(t, err, "Failed to create ImagePullOptions") //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, "eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwicGFzc3dvcmQiOiJwYXNzd29yZCJ9", options.RegistryAuth, "Username and Password should be provided")
config.SetDir("testdata/docker-pull-options")
@@ -56,6 +62,6 @@ func TestGetImagePullOptions(t *testing.T) {
options, err = getImagePullOptions(ctx, NewDockerPullExecutorInput{
Image: "nektos/act",
})
assert.Nil(t, err, "Failed to create ImagePullOptions")
assert.NoError(t, err, "Failed to create ImagePullOptions") //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, "eyJ1c2VybmFtZSI6InVzZXJuYW1lIiwicGFzc3dvcmQiOiJwYXNzd29yZFxuIiwic2VydmVyYWRkcmVzcyI6Imh0dHBzOi8vaW5kZXguZG9ja2VyLmlvL3YxLyJ9", options.RegistryAuth, "RegistryAuth should be taken from local docker config")
}

View File

@@ -1,4 +1,8 @@
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows))
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd))
package container
@@ -13,35 +17,32 @@ import (
"path/filepath"
"regexp"
"runtime"
"slices"
"strconv"
"strings"
"github.com/docker/docker/api/types/network"
networktypes "github.com/docker/docker/api/types/network"
"github.com/gobwas/glob"
"gitea.com/gitea/runner/act/common"
"gitea.com/gitea/runner/act/filecollector"
"dario.cat/mergo"
"github.com/Masterminds/semver"
cerrdefs "github.com/containerd/errdefs"
"github.com/docker/cli/cli/compose/loader"
"github.com/docker/cli/cli/connhelper"
"github.com/go-git/go-billy/v5/helper/polyfill"
"github.com/go-git/go-billy/v5/osfs"
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
"github.com/gobwas/glob"
"github.com/joho/godotenv"
"github.com/imdario/mergo"
"github.com/kballard/go-shellquote"
"github.com/spf13/pflag"
"github.com/docker/cli/cli/compose/loader"
"github.com/docker/cli/cli/connhelper"
"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/client"
"github.com/docker/docker/pkg/stdcopy"
"github.com/moby/moby/api/pkg/stdcopy"
"github.com/moby/moby/api/types/container"
"github.com/moby/moby/api/types/mount"
"github.com/moby/moby/api/types/network"
"github.com/moby/moby/api/types/system"
"github.com/moby/moby/client"
specs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/Masterminds/semver"
"golang.org/x/term"
"github.com/nektos/act/pkg/common"
"github.com/spf13/pflag"
)
// NewContainer creates a reference to a container
@@ -53,7 +54,7 @@ func NewContainer(input *NewContainerInput) ExecutionsEnvironment {
func (cr *containerReference) ConnectToNetwork(name string) common.Executor {
return common.
NewDebugExecutor("%sdocker network connect %s %s", logPrefix, name, cr.input.Name).
NewDebugExecutor("docker network connect %s %s", name, cr.input.Name).
Then(
common.NewPipelineExecutor(
cr.connect(),
@@ -64,9 +65,13 @@ func (cr *containerReference) ConnectToNetwork(name string) common.Executor {
func (cr *containerReference) connectToNetwork(name string, aliases []string) common.Executor {
return func(ctx context.Context) error {
return cr.cli.NetworkConnect(ctx, name, cr.input.Name, &networktypes.EndpointSettings{
Aliases: aliases,
_, err := cr.cli.NetworkConnect(ctx, name, client.NetworkConnectOptions{
Container: cr.input.Name,
EndpointConfig: &network.EndpointSettings{
Aliases: aliases,
},
})
return err
}
}
@@ -74,7 +79,7 @@ func (cr *containerReference) connectToNetwork(name string, aliases []string) co
// API version is 1.41 and beyond
func supportsContainerImagePlatform(ctx context.Context, cli client.APIClient) bool {
logger := common.Logger(ctx)
ver, err := cli.ServerVersion(ctx)
ver, err := cli.ServerVersion(ctx, client.ServerVersionOptions{})
if err != nil {
logger.Panicf("Failed to get Docker API Version: %s", err)
return false
@@ -88,9 +93,9 @@ func supportsContainerImagePlatform(ctx context.Context, cli client.APIClient) b
return constraint.Check(sv)
}
func (cr *containerReference) Create(capAdd []string, capDrop []string) common.Executor {
func (cr *containerReference) Create(capAdd, capDrop []string) common.Executor {
return common.
NewInfoExecutor("%sdocker create image=%s platform=%s entrypoint=%+q cmd=%+q", logPrefix, cr.input.Image, cr.input.Platform, cr.input.Entrypoint, cr.input.Cmd).
NewInfoExecutor("docker create image=%s platform=%s entrypoint=%+q cmd=%+q network=%+q", cr.input.Image, cr.input.Platform, cr.input.Entrypoint, cr.input.Cmd, cr.input.NetworkMode).
Then(
common.NewPipelineExecutor(
cr.connect(),
@@ -102,7 +107,7 @@ func (cr *containerReference) Create(capAdd []string, capDrop []string) common.E
func (cr *containerReference) Start(attach bool) common.Executor {
return common.
NewInfoExecutor("%sdocker run image=%s platform=%s entrypoint=%+q cmd=%+q", logPrefix, cr.input.Image, cr.input.Platform, cr.input.Entrypoint, cr.input.Cmd).
NewInfoExecutor("docker run image=%s platform=%s entrypoint=%+q cmd=%+q network=%+q", cr.input.Image, cr.input.Platform, cr.input.Entrypoint, cr.input.Cmd, cr.input.NetworkMode).
Then(
common.NewPipelineExecutor(
cr.connect(),
@@ -125,7 +130,7 @@ func (cr *containerReference) Start(attach bool) common.Executor {
func (cr *containerReference) Pull(forcePull bool) common.Executor {
return common.
NewInfoExecutor("%sdocker pull image=%s platform=%s username=%s forcePull=%t", logPrefix, cr.input.Image, cr.input.Platform, cr.input.Username, forcePull).
NewInfoExecutor("docker pull image=%s platform=%s username=%s forcePull=%t", cr.input.Image, cr.input.Platform, cr.input.Username, forcePull).
Then(
NewDockerPullExecutor(NewDockerPullExecutorInput{
Image: cr.input.Image,
@@ -145,9 +150,11 @@ func (cr *containerReference) Copy(destPath string, files ...*FileEntry) common.
).IfNot(common.Dryrun)
}
func (cr *containerReference) CopyDir(destPath string, srcPath string, useGitIgnore bool) common.Executor {
func (cr *containerReference) CopyDir(destPath, srcPath string, useGitIgnore bool) common.Executor {
return common.NewPipelineExecutor(
common.NewInfoExecutor("%sdocker cp src=%s dst=%s", logPrefix, srcPath, destPath),
common.NewInfoExecutor("docker cp src=%s dst=%s", srcPath, destPath),
cr.connect(),
cr.find(),
cr.copyDir(destPath, srcPath, useGitIgnore),
func(ctx context.Context) error {
// If this fails, then folders have wrong permissions on non root container
@@ -161,10 +168,23 @@ func (cr *containerReference) CopyDir(destPath string, srcPath string, useGitIgn
func (cr *containerReference) GetContainerArchive(ctx context.Context, srcPath string) (io.ReadCloser, error) {
if common.Dryrun(ctx) {
return nil, fmt.Errorf("DRYRUN is not supported in GetContainerArchive")
return nil, errors.New("DRYRUN is not supported in GetContainerArchive")
}
a, _, err := cr.cli.CopyFromContainer(ctx, cr.id, srcPath)
return a, err
// Direct entry point (no pipeline) — revalidate cr.id ourselves.
if err := cr.connect()(ctx); err != nil {
return nil, err
}
if err := cr.find()(ctx); err != nil {
return nil, err
}
if cr.id == "" {
return nil, cr.missingContainerError("get archive %s", srcPath)
}
result, err := cr.cli.CopyFromContainer(ctx, cr.id, client.CopyFromContainerOptions{SourcePath: srcPath})
if err != nil {
return nil, err
}
return result.Content, nil
}
func (cr *containerReference) UpdateFromEnv(srcPath string, env *map[string]string) common.Executor {
@@ -177,7 +197,7 @@ func (cr *containerReference) UpdateFromImageEnv(env *map[string]string) common.
func (cr *containerReference) Exec(command []string, env map[string]string, user, workdir string) common.Executor {
return common.NewPipelineExecutor(
common.NewInfoExecutor("%sdocker exec cmd=[%s] user=%s workdir=%s", logPrefix, strings.Join(command, " "), user, workdir),
common.NewInfoExecutor("docker exec cmd=[%s] user=%s workdir=%s", strings.Join(command, " "), user, workdir),
cr.connect(),
cr.find(),
cr.exec(command, env, user, workdir),
@@ -193,7 +213,7 @@ func (cr *containerReference) Remove() common.Executor {
).IfNot(common.Dryrun)
}
func (cr *containerReference) ReplaceLogWriter(stdout io.Writer, stderr io.Writer) (io.Writer, io.Writer) {
func (cr *containerReference) ReplaceLogWriter(stdout, stderr io.Writer) (io.Writer, io.Writer) {
out := cr.input.Stdout
err := cr.input.Stderr
@@ -222,22 +242,27 @@ func GetDockerClient(ctx context.Context) (cli client.APIClient, err error) {
if err != nil {
return nil, err
}
cli, err = client.NewClientWithOpts(
cli, err = client.New(
client.WithHost(helper.Host),
client.WithDialContext(helper.Dialer),
)
} else {
cli, err = client.NewClientWithOpts(client.FromEnv)
cli, err = client.New(client.FromEnv)
}
if err != nil {
return nil, fmt.Errorf("failed to connect to docker daemon: %w", err)
}
cli.NegotiateAPIVersion(ctx)
// Best-effort API version negotiation, matching the old client.NegotiateAPIVersion
// behaviour. Ping failures here are non-fatal: the connection is exercised again on
// the first real call, and the client falls back to the daemon's default API version.
if _, err := cli.Ping(ctx, client.PingOptions{NegotiateAPIVersion: true}); err != nil {
common.Logger(ctx).Warnf("docker daemon ping during version negotiation failed, continuing: %v", err)
}
return cli, nil
}
func GetHostInfo(ctx context.Context) (info types.Info, err error) {
func GetHostInfo(ctx context.Context) (info system.Info, err error) {
var cli client.APIClient
cli, err = GetDockerClient(ctx)
if err != nil {
@@ -245,12 +270,12 @@ func GetHostInfo(ctx context.Context) (info types.Info, err error) {
}
defer cli.Close()
info, err = cli.Info(ctx)
result, err := cli.Info(ctx, client.InfoOptions{})
if err != nil {
return info, err
}
return info, nil
return result.Info, nil
}
// Arch fetches values from docker info and translates architecture to
@@ -264,8 +289,10 @@ func RunnerArch(ctx context.Context) string {
archMapper := map[string]string{
"x86_64": "X64",
"amd64": "X64",
"386": "X86",
"aarch64": "ARM64",
"arm64": "ARM64",
}
if arch, ok := archMapper[info.Architecture]; ok {
return arch
@@ -300,19 +327,31 @@ func (cr *containerReference) Close() common.Executor {
}
}
// missingContainerError is the shared "container X does not exist" error
// used by ops that need a live cr.id.
func (cr *containerReference) missingContainerError(format string, args ...any) error {
return fmt.Errorf("container %q does not exist; cannot "+format, append([]any{cr.input.Name}, args...)...)
}
func (cr *containerReference) find() common.Executor {
return func(ctx context.Context) error {
if cr.id != "" {
return nil
// Validate cached id; clear only on definitive NotFound so a
// transient daemon error doesn't abort cleanup pipelines.
_, err := cr.cli.ContainerInspect(ctx, cr.id, client.ContainerInspectOptions{})
if !cerrdefs.IsNotFound(err) {
return nil
}
cr.id = ""
}
containers, err := cr.cli.ContainerList(ctx, types.ContainerListOptions{
containers, err := cr.cli.ContainerList(ctx, client.ContainerListOptions{
All: true,
})
if err != nil {
return fmt.Errorf("failed to list containers: %w", err)
}
for _, c := range containers {
for _, c := range containers.Items {
for _, name := range c.Names {
if name[1:] == cr.input.Name {
cr.id = c.ID
@@ -321,7 +360,6 @@ func (cr *containerReference) find() common.Executor {
}
}
cr.id = ""
return nil
}
}
@@ -333,7 +371,7 @@ func (cr *containerReference) remove() common.Executor {
}
logger := common.Logger(ctx)
err := cr.cli.ContainerRemove(ctx, cr.id, types.ContainerRemoveOptions{
_, err := cr.cli.ContainerRemove(ctx, cr.id, client.ContainerRemoveOptions{
RemoveVolumes: true,
Force: true,
})
@@ -369,15 +407,25 @@ func (cr *containerReference) mergeContainerConfigs(ctx context.Context, config
return nil, nil, fmt.Errorf("Cannot parse container options: '%s': '%w'", input.Options, err)
}
// If a service container's network is set to `host`, the container will not be able to
// connect to the specified network created for the job container and the service containers.
// So comment out the following code.
// FIXME: If everything is fine after gitea/act v0.260.0, remove the following comment.
// In the old fork version, the code is
// if len(copts.netMode.Value()) == 0 {
// if err = copts.netMode.Set("host"); err != nil {
// return nil, nil, fmt.Errorf("Cannot parse networkmode=host. This is an internal error and should not happen: '%w'", err)
// }
// }
// And it has been commented with:
// If a service container's network is set to `host`, the container will not be able to
// connect to the specified network created for the job container and the service containers.
// So comment out the following code.
// Not the if it's necessary to comment it in the new version,
// since it's cr.input.NetworkMode now.
if len(copts.netMode.Value()) == 0 {
if err = copts.netMode.Set(cr.input.NetworkMode); err != nil {
return nil, nil, fmt.Errorf("Cannot parse networkmode=%s. This is an internal error and should not happen: '%w'", cr.input.NetworkMode, err)
}
}
// If the `privileged` config has been disabled, `copts.privileged` need to be forced to false,
// even if the user specifies `--privileged` in the options string.
@@ -420,22 +468,31 @@ func (cr *containerReference) mergeContainerConfigs(ctx context.Context, config
return config, hostConfig, nil
}
func (cr *containerReference) create(capAdd []string, capDrop []string) common.Executor {
func (cr *containerReference) create(capAdd, capDrop []string) common.Executor {
return func(ctx context.Context) error {
if cr.id != "" {
return nil
}
logger := common.Logger(ctx)
isTerminal := term.IsTerminal(int(os.Stdout.Fd()))
input := cr.input
exposedPorts, err := convertPortSet(input.ExposedPorts)
if err != nil {
return err
}
portBindings, err := convertPortMap(input.PortBindings)
if err != nil {
return err
}
config := &container.Config{
Image: input.Image,
WorkingDir: input.WorkingDir,
Env: input.Env,
Tty: isTerminal,
Image: input.Image,
WorkingDir: input.WorkingDir,
Env: input.Env,
ExposedPorts: exposedPorts,
Tty: input.AllocatePTY,
}
logger.Debugf("Common container.Config ==> %+v", config)
// For Gitea, reduce log noise
// logger.Debugf("Common container.Config ==> %+v", config)
if len(input.Cmd) != 0 {
config.Cmd = input.Cmd
@@ -456,31 +513,27 @@ func (cr *containerReference) create(capAdd []string, capDrop []string) common.E
var platSpecs *specs.Platform
if supportsContainerImagePlatform(ctx, cr.cli) && cr.input.Platform != "" {
desiredPlatform := strings.SplitN(cr.input.Platform, `/`, 2)
if len(desiredPlatform) != 2 {
return fmt.Errorf("incorrect container platform option '%s'", cr.input.Platform)
}
platSpecs = &specs.Platform{
Architecture: desiredPlatform[1],
OS: desiredPlatform[0],
platSpecs, err = parsePlatform(cr.input.Platform)
if err != nil {
return err
}
}
hostConfig := &container.HostConfig{
CapAdd: capAdd,
CapDrop: capDrop,
Binds: input.Binds,
Mounts: mounts,
NetworkMode: container.NetworkMode(input.NetworkMode),
Privileged: input.Privileged,
UsernsMode: container.UsernsMode(input.UsernsMode),
AutoRemove: input.AutoRemove,
CapAdd: capAdd,
CapDrop: capDrop,
Binds: input.Binds,
Mounts: mounts,
NetworkMode: container.NetworkMode(input.NetworkMode),
Privileged: input.Privileged,
UsernsMode: container.UsernsMode(input.UsernsMode),
PortBindings: portBindings,
AutoRemove: input.AutoRemove,
}
logger.Debugf("Common container.HostConfig ==> %+v", hostConfig)
// For Gitea, reduce log noise
// logger.Debugf("Common container.HostConfig ==> %+v", hostConfig)
config, hostConfig, err := cr.mergeContainerConfigs(ctx, config, hostConfig)
config, hostConfig, err = cr.mergeContainerConfigs(ctx, config, hostConfig)
if err != nil {
return err
}
@@ -488,10 +541,12 @@ func (cr *containerReference) create(capAdd []string, capDrop []string) common.E
// For Gitea
config, hostConfig = cr.sanitizeConfig(ctx, config, hostConfig)
// For Gitea
// network-scoped alias is supported only for containers in user defined networks
var networkingConfig *network.NetworkingConfig
if hostConfig.NetworkMode.IsUserDefined() && len(input.NetworkAliases) > 0 {
// For Gitea, reduce log noise
// logger.Debugf("input.NetworkAliases ==> %v", input.NetworkAliases)
n := hostConfig.NetworkMode
// IsUserDefined and IsHost are broken on windows
if n.IsUserDefined() && n != "host" && len(input.NetworkAliases) > 0 {
endpointConfig := &network.EndpointSettings{
Aliases: input.NetworkAliases,
}
@@ -502,7 +557,13 @@ func (cr *containerReference) create(capAdd []string, capDrop []string) common.E
}
}
resp, err := cr.cli.ContainerCreate(ctx, config, hostConfig, networkingConfig, platSpecs, input.Name)
resp, err := cr.cli.ContainerCreate(ctx, client.ContainerCreateOptions{
Config: config,
HostConfig: hostConfig,
NetworkingConfig: networkingConfig,
Platform: platSpecs,
Name: input.Name,
})
if err != nil {
return fmt.Errorf("failed to create container: '%w'", err)
}
@@ -520,14 +581,20 @@ func (cr *containerReference) extractFromImageEnv(env *map[string]string) common
return func(ctx context.Context) error {
logger := common.Logger(ctx)
inspect, _, err := cr.cli.ImageInspectWithRaw(ctx, cr.input.Image)
inspect, err := cr.cli.ImageInspect(ctx, cr.input.Image)
if err != nil {
logger.Error(err)
return fmt.Errorf("inspect image: %w", err)
}
if inspect.Config == nil {
return nil
}
imageEnv, err := godotenv.Unmarshal(strings.Join(inspect.Config.Env, "\n"))
if err != nil {
logger.Error(err)
return fmt.Errorf("unmarshal image env: %w", err)
}
for k, v := range imageEnv {
@@ -549,6 +616,9 @@ func (cr *containerReference) extractFromImageEnv(env *map[string]string) common
func (cr *containerReference) exec(cmd []string, env map[string]string, user, workdir string) common.Executor {
return func(ctx context.Context) error {
if cr.id == "" {
return cr.missingContainerError("exec %v", cmd)
}
logger := common.Logger(ctx)
// Fix slashes when running on Windows
if runtime.GOOS == "windows" {
@@ -560,7 +630,7 @@ func (cr *containerReference) exec(cmd []string, env map[string]string, user, wo
}
logger.Debugf("Exec command '%s'", cmd)
isTerminal := term.IsTerminal(int(os.Stdout.Fd()))
isTerminal := cr.input.AllocatePTY
envList := make([]string, 0)
for k, v := range env {
envList = append(envList, fmt.Sprintf("%s=%s", k, v))
@@ -578,12 +648,12 @@ func (cr *containerReference) exec(cmd []string, env map[string]string, user, wo
}
logger.Debugf("Working directory '%s'", wd)
idResp, err := cr.cli.ContainerExecCreate(ctx, cr.id, types.ExecConfig{
idResp, err := cr.cli.ExecCreate(ctx, cr.id, client.ExecCreateOptions{
User: user,
Cmd: cmd,
WorkingDir: wd,
Env: envList,
Tty: isTerminal,
TTY: isTerminal,
AttachStderr: true,
AttachStdout: true,
})
@@ -591,38 +661,34 @@ func (cr *containerReference) exec(cmd []string, env map[string]string, user, wo
return fmt.Errorf("failed to create exec: %w", err)
}
resp, err := cr.cli.ContainerExecAttach(ctx, idResp.ID, types.ExecStartCheck{
Tty: isTerminal,
resp, err := cr.cli.ExecAttach(ctx, idResp.ID, client.ExecAttachOptions{
TTY: isTerminal,
})
if err != nil {
return fmt.Errorf("failed to attach to exec: %w", err)
}
defer resp.Close()
err = cr.waitForCommand(ctx, isTerminal, resp, idResp, user, workdir)
err = cr.waitForCommand(ctx, isTerminal, resp.HijackedResponse, idResp, user, workdir)
if err != nil {
return err
}
inspectResp, err := cr.cli.ContainerExecInspect(ctx, idResp.ID)
inspectResp, err := cr.cli.ExecInspect(ctx, idResp.ID, client.ExecInspectOptions{})
if err != nil {
return fmt.Errorf("failed to inspect exec: %w", err)
}
switch inspectResp.ExitCode {
case 0:
if inspectResp.ExitCode == 0 {
return nil
case 127:
return fmt.Errorf("exitcode '%d': command not found, please refer to https://github.com/nektos/act/issues/107 for more information", inspectResp.ExitCode)
default:
return fmt.Errorf("exitcode '%d': failure", inspectResp.ExitCode)
}
return ExitCodeError(inspectResp.ExitCode)
}
}
func (cr *containerReference) tryReadID(opt string, cbk func(id int)) common.Executor {
return func(ctx context.Context) error {
idResp, err := cr.cli.ContainerExecCreate(ctx, cr.id, types.ExecConfig{
idResp, err := cr.cli.ExecCreate(ctx, cr.id, client.ExecCreateOptions{
Cmd: []string{"id", opt},
AttachStdout: true,
AttachStderr: true,
@@ -631,7 +697,7 @@ func (cr *containerReference) tryReadID(opt string, cbk func(id int)) common.Exe
return nil
}
resp, err := cr.cli.ContainerExecAttach(ctx, idResp.ID, types.ExecStartCheck{})
resp, err := cr.cli.ExecAttach(ctx, idResp.ID, client.ExecAttachOptions{})
if err != nil {
return nil
}
@@ -661,7 +727,7 @@ func (cr *containerReference) tryReadGID() common.Executor {
return cr.tryReadID("-g", func(id int) { cr.GID = id })
}
func (cr *containerReference) waitForCommand(ctx context.Context, isTerminal bool, resp types.HijackedResponse, _ types.IDResponse, _ string, _ string) error {
func (cr *containerReference) waitForCommand(ctx context.Context, isTerminal bool, resp client.HijackedResponse, _ client.ExecCreateResult, _, _ string) error {
logger := common.Logger(ctx)
cmdResponse := make(chan error)
@@ -707,15 +773,45 @@ func (cr *containerReference) waitForCommand(ctx context.Context, isTerminal boo
}
func (cr *containerReference) CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error {
err := cr.cli.CopyToContainer(ctx, cr.id, destPath, tarStream, types.CopyToContainerOptions{})
if cr.id == "" {
return cr.missingContainerError("copy to %s", destPath)
}
// Mkdir
buf := &bytes.Buffer{}
tw := tar.NewWriter(buf)
_ = tw.WriteHeader(&tar.Header{
Name: destPath,
Mode: 0o777,
Typeflag: tar.TypeDir,
})
tw.Close()
_, err := cr.cli.CopyToContainer(ctx, cr.id, client.CopyToContainerOptions{
DestinationPath: "/",
Content: buf,
})
if err != nil {
return fmt.Errorf("failed to mkdir to copy content to container: %w", err)
}
// Copy Content
_, err = cr.cli.CopyToContainer(ctx, cr.id, client.CopyToContainerOptions{
DestinationPath: destPath,
Content: tarStream,
})
if err != nil {
return fmt.Errorf("failed to copy content to container: %w", err)
}
// If this fails, then folders have wrong permissions on non root container
if cr.UID != 0 || cr.GID != 0 {
_ = cr.Exec([]string{"chown", "-R", fmt.Sprintf("%d:%d", cr.UID, cr.GID), destPath}, nil, "0", "")(ctx)
}
return nil
}
func (cr *containerReference) copyDir(dstPath string, srcPath string, useGitIgnore bool) common.Executor {
func (cr *containerReference) copyDir(dstPath, srcPath string, useGitIgnore bool) common.Executor {
return func(ctx context.Context) error {
if cr.id == "" {
return cr.missingContainerError("copy directory to %s", dstPath)
}
logger := common.Logger(ctx)
tarFile, err := os.CreateTemp("", "act")
if err != nil {
@@ -751,12 +847,12 @@ func (cr *containerReference) copyDir(dstPath string, srcPath string, useGitIgno
ignorer = gitignore.NewMatcher(ps)
}
fc := &fileCollector{
Fs: &defaultFs{},
fc := &filecollector.FileCollector{
Fs: &filecollector.DefaultFs{},
Ignorer: ignorer,
SrcPath: srcPath,
SrcPrefix: srcPrefix,
Handler: &tarCollector{
Handler: &filecollector.TarCollector{
TarWriter: tw,
UID: cr.UID,
GID: cr.GID,
@@ -764,7 +860,7 @@ func (cr *containerReference) copyDir(dstPath string, srcPath string, useGitIgno
},
}
err = filepath.Walk(srcPath, fc.collectFiles(ctx, []string{}))
err = filepath.Walk(srcPath, fc.CollectFiles(ctx, []string{}))
if err != nil {
return err
}
@@ -777,7 +873,10 @@ func (cr *containerReference) copyDir(dstPath string, srcPath string, useGitIgno
if err != nil {
return fmt.Errorf("failed to seek tar archive: %w", err)
}
err = cr.cli.CopyToContainer(ctx, cr.id, "/", tarFile, types.CopyToContainerOptions{})
_, err = cr.cli.CopyToContainer(ctx, cr.id, client.CopyToContainerOptions{
DestinationPath: "/",
Content: tarFile,
})
if err != nil {
return fmt.Errorf("failed to copy content to container: %w", err)
}
@@ -787,6 +886,9 @@ func (cr *containerReference) copyDir(dstPath string, srcPath string, useGitIgno
func (cr *containerReference) copyContent(dstPath string, files ...*FileEntry) common.Executor {
return func(ctx context.Context) error {
if cr.id == "" {
return cr.missingContainerError("copy to %s", dstPath)
}
logger := common.Logger(ctx)
var buf bytes.Buffer
tw := tar.NewWriter(&buf)
@@ -811,7 +913,10 @@ func (cr *containerReference) copyContent(dstPath string, files ...*FileEntry) c
}
logger.Debugf("Extracting content to '%s'", dstPath)
err := cr.cli.CopyToContainer(ctx, cr.id, dstPath, &buf, types.CopyToContainerOptions{})
_, err := cr.cli.CopyToContainer(ctx, cr.id, client.CopyToContainerOptions{
DestinationPath: dstPath,
Content: &buf,
})
if err != nil {
return fmt.Errorf("failed to copy content to container: %w", err)
}
@@ -821,7 +926,7 @@ func (cr *containerReference) copyContent(dstPath string, files ...*FileEntry) c
func (cr *containerReference) attach() common.Executor {
return func(ctx context.Context) error {
out, err := cr.cli.ContainerAttach(ctx, cr.id, types.ContainerAttachOptions{
out, err := cr.cli.ContainerAttach(ctx, cr.id, client.ContainerAttachOptions{
Stream: true,
Stdout: true,
Stderr: true,
@@ -829,7 +934,7 @@ func (cr *containerReference) attach() common.Executor {
if err != nil {
return fmt.Errorf("failed to attach to container: %w", err)
}
isTerminal := term.IsTerminal(int(os.Stdout.Fd()))
isTerminal := cr.input.AllocatePTY
var outWriter io.Writer
outWriter = cr.input.Stdout
@@ -859,7 +964,7 @@ func (cr *containerReference) start() common.Executor {
logger := common.Logger(ctx)
logger.Debugf("Starting container: %v", cr.id)
if err := cr.cli.ContainerStart(ctx, cr.id, types.ContainerStartOptions{}); err != nil {
if _, err := cr.cli.ContainerStart(ctx, cr.id, client.ContainerStartOptions{}); err != nil {
return fmt.Errorf("failed to start container: %w", err)
}
@@ -871,14 +976,16 @@ func (cr *containerReference) start() common.Executor {
func (cr *containerReference) wait() common.Executor {
return func(ctx context.Context) error {
logger := common.Logger(ctx)
statusCh, errCh := cr.cli.ContainerWait(ctx, cr.id, container.WaitConditionNotRunning)
waitResult := cr.cli.ContainerWait(ctx, cr.id, client.ContainerWaitOptions{
Condition: container.WaitConditionNotRunning,
})
var statusCode int64
select {
case err := <-errCh:
case err := <-waitResult.Error:
if err != nil {
return fmt.Errorf("failed to wait for container: %w", err)
}
case status := <-statusCh:
case status := <-waitResult.Result:
statusCode = status.StatusCode
}
@@ -888,7 +995,7 @@ func (cr *containerReference) wait() common.Executor {
return nil
}
return fmt.Errorf("exit with `FAILURE`: %v", statusCode)
return ExitCodeError(statusCode)
}
}
@@ -898,22 +1005,7 @@ func (cr *containerReference) sanitizeConfig(ctx context.Context, config *contai
logger := common.Logger(ctx)
if len(cr.input.ValidVolumes) > 0 {
globs := make([]glob.Glob, 0, len(cr.input.ValidVolumes))
for _, v := range cr.input.ValidVolumes {
if g, err := glob.Compile(v); err != nil {
logger.Errorf("create glob from %s error: %v", v, err)
} else {
globs = append(globs, g)
}
}
isValid := func(v string) bool {
for _, g := range globs {
if g.Match(v) {
return true
}
}
return false
}
matcher := newValidVolumeMatcher(ctx, cr.input.ValidVolumes)
// sanitize binds
sanitizedBinds := make([]string, 0, len(hostConfig.Binds))
for _, bind := range hostConfig.Binds {
@@ -927,7 +1019,7 @@ func (cr *containerReference) sanitizeConfig(ctx context.Context, config *contai
sanitizedBinds = append(sanitizedBinds, bind)
continue
}
if isValid(parsed.Source) {
if matcher.isValid(parsed.Source, mount.Type(parsed.Type)) {
sanitizedBinds = append(sanitizedBinds, bind)
} else {
logger.Warnf("[%s] is not a valid volume, will be ignored", parsed.Source)
@@ -937,7 +1029,7 @@ func (cr *containerReference) sanitizeConfig(ctx context.Context, config *contai
// sanitize mounts
sanitizedMounts := make([]mount.Mount, 0, len(hostConfig.Mounts))
for _, mt := range hostConfig.Mounts {
if isValid(mt.Source) {
if matcher.isValid(mt.Source, mt.Type) {
sanitizedMounts = append(sanitizedMounts, mt)
} else {
logger.Warnf("[%s] is not a valid volume, will be ignored", mt.Source)
@@ -951,3 +1043,129 @@ func (cr *containerReference) sanitizeConfig(ctx context.Context, config *contai
return config, hostConfig
}
type validVolumeMatcher struct {
allowAll bool
named []glob.Glob
host []glob.Glob
}
func newValidVolumeMatcher(ctx context.Context, validVolumes []string) validVolumeMatcher {
logger := common.Logger(ctx)
ret := validVolumeMatcher{
named: make([]glob.Glob, 0, len(validVolumes)),
host: make([]glob.Glob, 0, len(validVolumes)),
}
for _, v := range validVolumes {
if v == "**" {
ret.allowAll = true
continue
}
if !isHostVolumePattern(v) {
if g, err := glob.Compile(v); err != nil {
logger.Errorf("create glob from %s error: %v", v, err)
} else {
ret.named = append(ret.named, g)
}
continue
}
normalized, err := normalizeHostVolumePath(v)
if err != nil {
logger.Errorf("normalize volume pattern %s error: %v", v, err)
continue
}
if g, err := glob.Compile(normalized); err != nil {
logger.Errorf("create glob from %s error: %v", normalized, err)
} else {
ret.host = append(ret.host, g)
}
}
return ret
}
func (m validVolumeMatcher) isValid(source string, sourceType mount.Type) bool {
if m.allowAll {
return true
}
if isHostVolumeSource(source, sourceType) {
normalized, err := normalizeHostVolumePath(source)
if err != nil {
return false
}
for _, g := range m.host {
if g.Match(normalized) {
return true
}
}
return false
}
for _, g := range m.named {
if g.Match(source) {
return true
}
}
return false
}
func isHostVolumePattern(pattern string) bool {
return filepath.IsAbs(pattern) ||
strings.HasPrefix(pattern, "."+string(filepath.Separator)) ||
strings.HasPrefix(pattern, ".."+string(filepath.Separator)) ||
strings.Contains(pattern, "/") ||
strings.Contains(pattern, `\`)
}
func isHostVolumeSource(source string, sourceType mount.Type) bool {
if sourceType == mount.TypeBind {
return true
}
if sourceType == mount.TypeVolume {
return false
}
return isHostVolumePattern(source)
}
func normalizeHostVolumePath(path string) (string, error) {
abs, err := filepath.Abs(path)
if err != nil {
return "", err
}
return evalSymlinksExistingPrefix(abs)
}
func evalSymlinksExistingPrefix(path string) (string, error) {
resolved, err := filepath.EvalSymlinks(path)
if err == nil {
return filepath.Clean(resolved), nil
}
if !errors.Is(err, os.ErrNotExist) {
return "", err
}
current := path
var missing []string
for {
_, err := os.Lstat(current)
if err == nil {
resolved, err := filepath.EvalSymlinks(current)
if err != nil {
return "", err
}
for _, name := range slices.Backward(missing) {
resolved = filepath.Join(resolved, name)
}
return filepath.Clean(resolved), nil
}
if !errors.Is(err, os.ErrNotExist) {
return "", err
}
parent := filepath.Dir(current)
if parent == current {
return filepath.Clean(path), nil
}
missing = append(missing, filepath.Base(current))
current = parent
}
}

View File

@@ -0,0 +1,551 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
"bufio"
"bytes"
"context"
"errors"
"io"
"net"
"os"
"path/filepath"
"strings"
"testing"
"time"
"gitea.com/gitea/runner/act/common"
cerrdefs "github.com/containerd/errdefs"
"github.com/moby/moby/api/types/container"
mobyclient "github.com/moby/moby/client"
"github.com/sirupsen/logrus/hooks/test"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)
func TestDocker(t *testing.T) {
requireDocker(t)
ctx := context.Background()
client, err := GetDockerClient(ctx)
require.NoError(t, err)
defer client.Close()
dockerBuild := NewDockerBuildExecutor(NewDockerBuildExecutorInput{
ContextDir: "testdata",
ImageTag: "envmergetest",
})
err = dockerBuild(ctx)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
cr := &containerReference{
cli: client,
input: &NewContainerInput{
Image: "envmergetest",
},
}
env := map[string]string{
"PATH": "/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin",
"RANDOM_VAR": "WITH_VALUE",
"ANOTHER_VAR": "",
"CONFLICT_VAR": "I_EXIST_IN_MULTIPLE_PLACES",
}
envExecutor := cr.extractFromImageEnv(&env)
err = envExecutor(ctx)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, map[string]string{
"PATH": "/usr/local/bin:/usr/bin:/usr/sbin:/bin:/sbin:/this/path/does/not/exists/anywhere:/this/either",
"RANDOM_VAR": "WITH_VALUE",
"ANOTHER_VAR": "",
"SOME_RANDOM_VAR": "",
"ANOTHER_ONE": "BUT_I_HAVE_VALUE",
"CONFLICT_VAR": "I_EXIST_IN_MULTIPLE_PLACES",
}, env)
}
type mockDockerClient struct {
mobyclient.APIClient
mock.Mock
}
func (m *mockDockerClient) ExecCreate(ctx context.Context, id string, opts mobyclient.ExecCreateOptions) (mobyclient.ExecCreateResult, error) {
args := m.Called(ctx, id, opts)
return args.Get(0).(mobyclient.ExecCreateResult), args.Error(1)
}
func (m *mockDockerClient) ExecAttach(ctx context.Context, id string, opts mobyclient.ExecAttachOptions) (mobyclient.ExecAttachResult, error) {
args := m.Called(ctx, id, opts)
return args.Get(0).(mobyclient.ExecAttachResult), args.Error(1)
}
func (m *mockDockerClient) ExecInspect(ctx context.Context, execID string, opts mobyclient.ExecInspectOptions) (mobyclient.ExecInspectResult, error) {
args := m.Called(ctx, execID, opts)
return args.Get(0).(mobyclient.ExecInspectResult), args.Error(1)
}
func (m *mockDockerClient) ContainerWait(ctx context.Context, containerID string, opts mobyclient.ContainerWaitOptions) mobyclient.ContainerWaitResult {
args := m.Called(ctx, containerID, opts)
return args.Get(0).(mobyclient.ContainerWaitResult)
}
func (m *mockDockerClient) CopyToContainer(ctx context.Context, id string, options mobyclient.CopyToContainerOptions) (mobyclient.CopyToContainerResult, error) {
args := m.Called(ctx, id, options)
return args.Get(0).(mobyclient.CopyToContainerResult), args.Error(1)
}
func (m *mockDockerClient) ContainerInspect(ctx context.Context, id string, opts mobyclient.ContainerInspectOptions) (mobyclient.ContainerInspectResult, error) {
args := m.Called(ctx, id, opts)
return args.Get(0).(mobyclient.ContainerInspectResult), args.Error(1)
}
func (m *mockDockerClient) ContainerList(ctx context.Context, opts mobyclient.ContainerListOptions) (mobyclient.ContainerListResult, error) {
args := m.Called(ctx, opts)
return args.Get(0).(mobyclient.ContainerListResult), args.Error(1)
}
type endlessReader struct {
io.Reader
}
func (r endlessReader) Read(_ []byte) (n int, err error) {
return 1, nil
}
type mockConn struct {
net.Conn
mock.Mock
}
func (m *mockConn) Write(b []byte) (n int, err error) {
args := m.Called(b)
return args.Int(0), args.Error(1)
}
func (m *mockConn) Close() (err error) {
return nil
}
func TestDockerExecAbort(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
conn := &mockConn{}
conn.On("Write", mock.AnythingOfType("[]uint8")).Return(1, nil)
client := &mockDockerClient{}
client.On("ExecCreate", ctx, "123", mock.AnythingOfType("client.ExecCreateOptions")).Return(mobyclient.ExecCreateResult{ID: "id"}, nil)
client.On("ExecAttach", ctx, "id", mock.AnythingOfType("client.ExecAttachOptions")).Return(mobyclient.ExecAttachResult{
HijackedResponse: mobyclient.HijackedResponse{
Conn: conn,
Reader: bufio.NewReader(endlessReader{}),
},
}, nil)
cr := &containerReference{
id: "123",
cli: client,
input: &NewContainerInput{
Image: "image",
},
}
channel := make(chan error)
go func() {
channel <- cr.exec([]string{""}, map[string]string{}, "user", "workdir")(ctx)
}()
time.Sleep(500 * time.Millisecond)
cancel()
err := <-channel
assert.ErrorIs(t, err, context.Canceled) //nolint:testifylint // pre-existing issue from nektos/act
conn.AssertExpectations(t)
client.AssertExpectations(t)
}
func TestDockerExecFailure(t *testing.T) {
ctx := context.Background()
conn := &mockConn{}
client := &mockDockerClient{}
client.On("ExecCreate", ctx, "123", mock.AnythingOfType("client.ExecCreateOptions")).Return(mobyclient.ExecCreateResult{ID: "id"}, nil)
client.On("ExecAttach", ctx, "id", mock.AnythingOfType("client.ExecAttachOptions")).Return(mobyclient.ExecAttachResult{
HijackedResponse: mobyclient.HijackedResponse{
Conn: conn,
Reader: bufio.NewReader(strings.NewReader("output")),
},
}, nil)
client.On("ExecInspect", ctx, "id", mobyclient.ExecInspectOptions{}).Return(mobyclient.ExecInspectResult{
ExitCode: 1,
}, nil)
cr := &containerReference{
id: "123",
cli: client,
input: &NewContainerInput{
Image: "image",
},
}
err := cr.exec([]string{""}, map[string]string{}, "user", "workdir")(ctx)
var exitErr ExitCodeError
require.ErrorAs(t, err, &exitErr)
assert.Equal(t, ExitCodeError(1), exitErr)
assert.Equal(t, "Process completed with exit code 1.", err.Error())
conn.AssertExpectations(t)
client.AssertExpectations(t)
}
func TestDockerWaitFailure(t *testing.T) {
ctx := context.Background()
statusCh := make(chan container.WaitResponse, 1)
statusCh <- container.WaitResponse{StatusCode: 2}
errCh := make(chan error, 1)
client := &mockDockerClient{}
client.On("ContainerWait", ctx, "123", mobyclient.ContainerWaitOptions{Condition: container.WaitConditionNotRunning}).
Return(mobyclient.ContainerWaitResult{
Result: (<-chan container.WaitResponse)(statusCh),
Error: (<-chan error)(errCh),
})
cr := &containerReference{
id: "123",
cli: client,
input: &NewContainerInput{
Image: "image",
},
}
err := cr.wait()(ctx)
var exitErr ExitCodeError
require.ErrorAs(t, err, &exitErr)
assert.Equal(t, ExitCodeError(2), exitErr)
assert.Equal(t, "Process completed with exit code 2.", err.Error())
client.AssertExpectations(t)
}
func TestDockerCopyTarStream(t *testing.T) {
ctx := context.Background()
client := &mockDockerClient{}
client.On("CopyToContainer", ctx, "123", mock.MatchedBy(func(opts mobyclient.CopyToContainerOptions) bool {
return opts.DestinationPath == "/" && opts.Content != nil
})).Return(mobyclient.CopyToContainerResult{}, nil)
client.On("CopyToContainer", ctx, "123", mock.MatchedBy(func(opts mobyclient.CopyToContainerOptions) bool {
return opts.DestinationPath == "/var/run/act" && opts.Content != nil
})).Return(mobyclient.CopyToContainerResult{}, nil)
cr := &containerReference{
id: "123",
cli: client,
input: &NewContainerInput{
Image: "image",
},
}
_ = cr.CopyTarStream(ctx, "/var/run/act", &bytes.Buffer{})
client.AssertExpectations(t)
}
func TestDockerCopyTarStreamErrorInCopyFiles(t *testing.T) {
ctx := context.Background()
merr := errors.New("Failure")
client := &mockDockerClient{}
client.On("CopyToContainer", ctx, "123", mock.MatchedBy(func(opts mobyclient.CopyToContainerOptions) bool {
return opts.DestinationPath == "/" && opts.Content != nil
})).Return(mobyclient.CopyToContainerResult{}, merr)
cr := &containerReference{
id: "123",
cli: client,
input: &NewContainerInput{
Image: "image",
},
}
err := cr.CopyTarStream(ctx, "/var/run/act", &bytes.Buffer{})
assert.ErrorIs(t, err, merr) //nolint:testifylint // pre-existing issue from nektos/act
client.AssertExpectations(t)
}
func TestDockerCopyTarStreamErrorInMkdir(t *testing.T) {
ctx := context.Background()
merr := errors.New("Failure")
client := &mockDockerClient{}
client.On("CopyToContainer", ctx, "123", mock.MatchedBy(func(opts mobyclient.CopyToContainerOptions) bool {
return opts.DestinationPath == "/" && opts.Content != nil
})).Return(mobyclient.CopyToContainerResult{}, nil)
client.On("CopyToContainer", ctx, "123", mock.MatchedBy(func(opts mobyclient.CopyToContainerOptions) bool {
return opts.DestinationPath == "/var/run/act" && opts.Content != nil
})).Return(mobyclient.CopyToContainerResult{}, merr)
cr := &containerReference{
id: "123",
cli: client,
input: &NewContainerInput{
Image: "image",
},
}
err := cr.CopyTarStream(ctx, "/var/run/act", &bytes.Buffer{})
assert.ErrorIs(t, err, merr) //nolint:testifylint // pre-existing issue from nektos/act
client.AssertExpectations(t)
}
// find() must drop a stale cached id so later Copy/Exec don't hit the
// daemon with a torn-down container.
func TestFindRevalidatesStaleID(t *testing.T) {
ctx := context.Background()
notFound := cerrdefs.ErrNotFound.WithMessage("No such container")
boom := errors.New("daemon unreachable")
newCR := func(id string) (*containerReference, *mockDockerClient) {
client := &mockDockerClient{}
return &containerReference{id: id, cli: client, input: &NewContainerInput{Name: "job-1"}}, client
}
listOpts := mobyclient.ContainerListOptions{All: true}
inspectOpts := mobyclient.ContainerInspectOptions{}
t.Run("stale id cleared, name lookup empty", func(t *testing.T) {
cr, client := newCR("stale")
client.On("ContainerInspect", ctx, "stale", inspectOpts).Return(mobyclient.ContainerInspectResult{}, notFound)
client.On("ContainerList", ctx, listOpts).Return(mobyclient.ContainerListResult{}, nil)
require.NoError(t, cr.find()(ctx))
assert.Empty(t, cr.id)
client.AssertExpectations(t)
})
t.Run("stale id cleared, name lookup repopulates", func(t *testing.T) {
cr, client := newCR("stale")
client.On("ContainerInspect", ctx, "stale", inspectOpts).Return(mobyclient.ContainerInspectResult{}, notFound)
client.On("ContainerList", ctx, listOpts).Return(mobyclient.ContainerListResult{Items: []container.Summary{
{ID: "other", Names: []string{"/somebody-else"}},
{ID: "fresh", Names: []string{"/job-1"}},
}}, nil)
require.NoError(t, cr.find()(ctx))
assert.Equal(t, "fresh", cr.id)
client.AssertExpectations(t)
})
t.Run("live id kept", func(t *testing.T) {
cr, client := newCR("live")
client.On("ContainerInspect", ctx, "live", inspectOpts).Return(mobyclient.ContainerInspectResult{}, nil)
require.NoError(t, cr.find()(ctx))
assert.Equal(t, "live", cr.id)
client.AssertExpectations(t)
})
t.Run("transient inspect error trusts cache", func(t *testing.T) {
cr, client := newCR("live")
client.On("ContainerInspect", ctx, "live", inspectOpts).Return(mobyclient.ContainerInspectResult{}, boom)
require.NoError(t, cr.find()(ctx))
assert.Equal(t, "live", cr.id)
client.AssertExpectations(t)
})
t.Run("list error propagates", func(t *testing.T) {
cr, client := newCR("")
client.On("ContainerList", ctx, listOpts).Return(mobyclient.ContainerListResult{}, boom)
require.ErrorIs(t, cr.find()(ctx), boom)
client.AssertExpectations(t)
})
}
// Every daemon entry point fails fast with a clear, container-named
// error when no live cr.id is known.
func TestRejectsMissingContainer(t *testing.T) {
ctx := context.Background()
client := &mockDockerClient{}
client.On("ContainerList", ctx, mobyclient.ContainerListOptions{All: true}).Return(mobyclient.ContainerListResult{}, nil)
cr := &containerReference{cli: client, input: &NewContainerInput{Name: "job-1"}}
check := func(op string, err error) {
t.Helper()
require.Error(t, err, op)
assert.Contains(t, err.Error(), `container "job-1" does not exist`, op)
}
check("copyContent", cr.copyContent("/var/run/act", &FileEntry{Name: "x", Mode: 0o644})(ctx))
check("copyDir", cr.copyDir("/var/run/act", "/src", false)(ctx))
check("CopyTarStream", cr.CopyTarStream(ctx, "/var/run/act", &bytes.Buffer{}))
check("exec", cr.exec([]string{"echo"}, nil, "", "")(ctx))
_, err := cr.GetContainerArchive(ctx, "/var/run/act/x")
check("GetContainerArchive", err)
}
// End-to-end: a stale cr.id is cleared, repopulated from name lookup,
// and the Copy completes against the fresh id.
func TestPublicCopyPipelineHandlesStaleID(t *testing.T) {
ctx := context.Background()
client := &mockDockerClient{}
client.On("ContainerInspect", ctx, "stale", mobyclient.ContainerInspectOptions{}).
Return(mobyclient.ContainerInspectResult{}, cerrdefs.ErrNotFound.WithMessage("gone"))
client.On("ContainerList", ctx, mobyclient.ContainerListOptions{All: true}).
Return(mobyclient.ContainerListResult{Items: []container.Summary{
{ID: "fresh", Names: []string{"/job-1"}},
}}, nil)
client.On("CopyToContainer", ctx, "fresh", mock.MatchedBy(func(opts mobyclient.CopyToContainerOptions) bool {
return opts.DestinationPath == "/var/run/act"
})).Return(mobyclient.CopyToContainerResult{}, nil)
cr := &containerReference{id: "stale", cli: client, input: &NewContainerInput{Name: "job-1"}}
require.NoError(t, cr.Copy("/var/run/act", &FileEntry{Name: "x", Mode: 0o644})(ctx))
assert.Equal(t, "fresh", cr.id)
client.AssertExpectations(t)
}
// TestDockerCopyToSymlinkPath is a regression test for gitea/runner#981. Most base images
// symlink /var/run to /run, so copying into /var/run/act traverses that symlink. The broken
// docker 29.5.1 daemon fails the extraction with "mkdirat var/run: file exists" (fixed in
// 29.5.2). Running against the daemon shipped in the dind image, this catches a bad bump.
func TestDockerCopyToSymlinkPath(t *testing.T) {
requireDocker(t)
ctx := context.Background()
rc := NewContainer(&NewContainerInput{
Image: "alpine:latest",
Entrypoint: []string{"sleep", "30"},
Name: "act-test-symlink-" + time.Now().Format("20060102150405.000000"),
AutoRemove: true,
})
require.NoError(t, rc.Pull(false)(ctx))
require.NoError(t, rc.Create(nil, nil)(ctx))
require.NoError(t, rc.Start(false)(ctx))
t.Cleanup(func() {
_ = rc.Remove()(ctx)
_ = rc.Close()(ctx)
})
// CopyTarStream first creates the destination directory by extracting a tar at "/",
// which makes the daemon mkdir var, then var/run (the symlink), then act — the exact
// step that fails on the broken daemon.
err := rc.CopyTarStream(ctx, "/var/run/act", &bytes.Buffer{})
require.NoError(t, err)
}
// Type assert containerReference implements ExecutionsEnvironment
var _ ExecutionsEnvironment = &containerReference{}
func TestCheckVolumes(t *testing.T) {
testCases := []struct {
desc string
validVolumes []string
binds []string
expectedBinds []string
}{
{
desc: "match all volumes",
validVolumes: []string{"**"},
binds: []string{
"shared_volume:/shared_volume",
"/home/test/data:/test_data",
"/etc/conf.d/base.json:/config/base.json",
"sql_data:/sql_data",
"/secrets/keys:/keys",
},
expectedBinds: []string{
"shared_volume:/shared_volume",
"/home/test/data:/test_data",
"/etc/conf.d/base.json:/config/base.json",
"sql_data:/sql_data",
"/secrets/keys:/keys",
},
},
{
desc: "no volumes can be matched",
validVolumes: []string{},
binds: []string{
"shared_volume:/shared_volume",
"/home/test/data:/test_data",
"/etc/conf.d/base.json:/config/base.json",
"sql_data:/sql_data",
"/secrets/keys:/keys",
},
expectedBinds: []string{},
},
{
desc: "only allowed volumes can be matched",
validVolumes: []string{
"shared_volume",
"/home/test/data",
"/etc/conf.d/*.json",
},
binds: []string{
"shared_volume:/shared_volume",
"/home/test/data:/test_data",
"/etc/conf.d/base.json:/config/base.json",
"sql_data:/sql_data",
"/secrets/keys:/keys",
},
expectedBinds: []string{
"shared_volume:/shared_volume",
"/home/test/data:/test_data",
"/etc/conf.d/base.json:/config/base.json",
},
},
}
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
logger, _ := test.NewNullLogger()
ctx := common.WithLogger(context.Background(), logger)
cr := &containerReference{
input: &NewContainerInput{
ValidVolumes: tc.validVolumes,
},
}
_, hostConf := cr.sanitizeConfig(ctx, &container.Config{}, &container.HostConfig{Binds: tc.binds})
assert.Equal(t, tc.expectedBinds, hostConf.Binds)
})
}
}
func TestCheckVolumesRejectsEscapingHostPaths(t *testing.T) {
logger, _ := test.NewNullLogger()
ctx := common.WithLogger(context.Background(), logger)
base := t.TempDir()
allowed := filepath.Join(base, "allowed")
denied := filepath.Join(base, "denied")
require.NoError(t, os.MkdirAll(allowed, 0o700))
require.NoError(t, os.MkdirAll(denied, 0o700))
cr := &containerReference{
input: &NewContainerInput{
ValidVolumes: []string{filepath.Join(allowed, "**")},
},
}
escapingPath := allowed + string(filepath.Separator) + ".." + string(filepath.Separator) + "denied"
_, hostConf := cr.sanitizeConfig(ctx, &container.Config{}, &container.HostConfig{
Binds: []string{escapingPath + ":/mnt"},
})
assert.Empty(t, hostConf.Binds)
linkPath := filepath.Join(allowed, "link")
if err := os.Symlink(denied, linkPath); err != nil {
t.Skipf("cannot create symlink: %v", err)
}
_, hostConf = cr.sanitizeConfig(ctx, &container.Config{}, &container.HostConfig{
Binds: []string{linkPath + ":/mnt"},
})
assert.Empty(t, hostConf.Binds)
_, hostConf = cr.sanitizeConfig(ctx, &container.Config{}, &container.HostConfig{
Binds: []string{filepath.Join(linkPath, "missing") + ":/mnt"},
})
assert.Empty(t, hostConf.Binds)
}

View File

@@ -0,0 +1,138 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// Copyright 2024 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
"fmt"
"os"
"path/filepath"
"strings"
log "github.com/sirupsen/logrus"
)
var CommonSocketLocations = []string{
"/var/run/docker.sock",
"/run/podman/podman.sock",
"$HOME/.colima/docker.sock",
"$XDG_RUNTIME_DIR/docker.sock",
"$XDG_RUNTIME_DIR/podman/podman.sock",
`\\.\pipe\docker_engine`,
"$HOME/.docker/run/docker.sock",
}
// returns socket URI or false if not found any
func socketLocation() (string, bool) {
if dockerHost, exists := os.LookupEnv("DOCKER_HOST"); exists {
return dockerHost, true
}
for _, p := range CommonSocketLocations {
if _, err := os.Lstat(os.ExpandEnv(p)); err == nil {
if strings.HasPrefix(p, `\\.\`) {
return "npipe://" + filepath.ToSlash(os.ExpandEnv(p)), true
}
return "unix://" + filepath.ToSlash(os.ExpandEnv(p)), true
}
}
return "", false
}
// This function, `isDockerHostURI`, takes a string argument `daemonPath`. It checks if the
// `daemonPath` is a valid Docker host URI. It does this by checking if the scheme of the URI (the
// part before "://") contains only alphabetic characters. If it does, the function returns true,
// indicating that the `daemonPath` is a Docker host URI. If it doesn't, or if the "://" delimiter
// is not found in the `daemonPath`, the function returns false.
func isDockerHostURI(daemonPath string) bool {
if before, _, ok := strings.Cut(daemonPath, "://"); ok {
scheme := before
if strings.IndexFunc(scheme, func(r rune) bool {
return (r < 'a' || r > 'z') && (r < 'A' || r > 'Z')
}) == -1 {
return true
}
}
return false
}
type SocketAndHost struct {
Socket string
Host string
}
func GetSocketAndHost(containerSocket string) (SocketAndHost, error) {
log.Debugf("Handling container host and socket")
// Prefer DOCKER_HOST, don't override it
dockerHost, hasDockerHost := socketLocation()
socketHost := SocketAndHost{Socket: containerSocket, Host: dockerHost}
// ** socketHost.Socket cases **
// Case 1: User does _not_ want to mount a daemon socket (passes a dash)
// Case 2: User passes a filepath to the socket; is that even valid?
// Case 3: User passes a valid socket; do nothing
// Case 4: User omitted the flag; set a sane default
// ** DOCKER_HOST cases **
// Case A: DOCKER_HOST is set; use it, i.e. do nothing
// Case B: DOCKER_HOST is empty; use sane defaults
// Set host for sanity's sake, when the socket isn't useful
if !hasDockerHost && (socketHost.Socket == "-" || !isDockerHostURI(socketHost.Socket) || socketHost.Socket == "") {
// Cases: 1B, 2B, 4B
socket, found := socketLocation()
socketHost.Host = socket
hasDockerHost = found
}
// A - (dash) in socketHost.Socket means don't mount, preserve this value
// otherwise if socketHost.Socket is a filepath don't use it as socket
// Exit early if we're in an invalid state (e.g. when no DOCKER_HOST and user supplied "-", a dash or omitted)
if !hasDockerHost && socketHost.Socket != "" && !isDockerHostURI(socketHost.Socket) {
// Cases: 1B, 2B
// Should we early-exit here, since there is no host nor socket to talk to?
return SocketAndHost{}, fmt.Errorf("DOCKER_HOST was not set, couldn't be found in the usual locations, and the container daemon socket ('%s') is invalid", socketHost.Socket)
}
// Default to DOCKER_HOST if set
if socketHost.Socket == "" && hasDockerHost {
// Cases: 4A
log.Debugf("Defaulting container socket to DOCKER_HOST")
socketHost.Socket = socketHost.Host
}
// Set sane default socket location if user omitted it
if socketHost.Socket == "" {
// Cases: 4B
socket, _ := socketLocation()
// socket is empty if it isn't found, so assignment here is at worst a no-op
log.Debugf("Defaulting container socket to default '%s'", socket)
socketHost.Socket = socket
}
// Exit if both the DOCKER_HOST and socket are fulfilled
if hasDockerHost {
// Cases: 1A, 2A, 3A, 4A
if !isDockerHostURI(socketHost.Socket) {
// Cases: 1A, 2A
log.Debugf("DOCKER_HOST is set, but socket is invalid '%s'", socketHost.Socket)
}
return socketHost, nil
}
// Set a sane DOCKER_HOST default if we can
if isDockerHostURI(socketHost.Socket) {
// Cases: 3B
log.Debugf("Setting DOCKER_HOST to container socket '%s'", socketHost.Socket)
socketHost.Host = socketHost.Socket
// Both DOCKER_HOST and container socket are valid; short-circuit exit
return socketHost, nil
}
// Here there is no DOCKER_HOST _and_ the supplied container socket is not a valid URI (either invalid or a file path)
// Cases: 2B <- but is already handled at the top
// I.e. this path should never be taken
return SocketAndHost{}, fmt.Errorf("no DOCKER_HOST and an invalid container socket '%s'", socketHost.Socket)
}

View File

@@ -0,0 +1,167 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// Copyright 2024 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
"os"
"testing"
log "github.com/sirupsen/logrus"
assert "github.com/stretchr/testify/assert"
)
func init() {
log.SetLevel(log.DebugLevel)
}
var originalCommonSocketLocations = CommonSocketLocations
func isolateSocketEnv(t *testing.T) {
t.Helper()
t.Cleanup(func() { CommonSocketLocations = originalCommonSocketLocations })
if host, ok := os.LookupEnv("DOCKER_HOST"); ok {
t.Setenv("DOCKER_HOST", host)
} else {
t.Cleanup(func() { os.Unsetenv("DOCKER_HOST") })
}
}
func TestGetSocketAndHostWithSocket(t *testing.T) {
// Arrange
isolateSocketEnv(t)
dockerHost := "unix:///my/docker/host.sock"
socketURI := "/path/to/my.socket"
t.Setenv("DOCKER_HOST", dockerHost)
// Act
ret, err := GetSocketAndHost(socketURI)
// Assert
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, SocketAndHost{socketURI, dockerHost}, ret)
}
func TestGetSocketAndHostNoSocket(t *testing.T) {
// Arrange
dockerHost := "unix:///my/docker/host.sock"
t.Setenv("DOCKER_HOST", dockerHost)
// Act
ret, err := GetSocketAndHost("")
// Assert
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, SocketAndHost{dockerHost, dockerHost}, ret)
}
func TestGetSocketAndHostOnlySocket(t *testing.T) {
// Arrange
isolateSocketEnv(t)
socketURI := "/path/to/my.socket"
os.Unsetenv("DOCKER_HOST")
defaultSocket, defaultSocketFound := socketLocation()
// Act
ret, err := GetSocketAndHost(socketURI)
// Assert
assert.NoError(t, err, "Expected no error from GetSocketAndHost") //nolint:testifylint // pre-existing issue from nektos/act
assert.True(t, defaultSocketFound, "Expected to find default socket")
assert.Equal(t, socketURI, ret.Socket, "Expected socket to match common location")
assert.Equal(t, defaultSocket, ret.Host, "Expected ret.Host to match default socket location")
}
func TestGetSocketAndHostDontMount(t *testing.T) {
// Arrange
isolateSocketEnv(t)
dockerHost := "unix:///my/docker/host.sock"
t.Setenv("DOCKER_HOST", dockerHost)
// Act
ret, err := GetSocketAndHost("-")
// Assert
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, SocketAndHost{"-", dockerHost}, ret)
}
func TestGetSocketAndHostNoHostNoSocket(t *testing.T) {
// Arrange
isolateSocketEnv(t)
os.Unsetenv("DOCKER_HOST")
defaultSocket, found := socketLocation()
// Act
ret, err := GetSocketAndHost("")
// Assert
assert.True(t, found, "Expected a default socket to be found")
assert.NoError(t, err, "Expected no error from GetSocketAndHost") //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, SocketAndHost{defaultSocket, defaultSocket}, ret, "Expected to match default socket location")
}
// Catch
// > Your code breaks setting DOCKER_HOST if shouldMount is false.
// > This happens if neither DOCKER_HOST nor --container-daemon-socket has a value, but socketLocation() returns a URI
func TestGetSocketAndHostNoHostNoSocketDefaultLocation(t *testing.T) {
// Arrange
isolateSocketEnv(t)
mySocketFile, tmpErr := os.CreateTemp(t.TempDir(), "act-*.sock")
mySocket := mySocketFile.Name()
unixSocket := "unix://" + mySocket
defer os.RemoveAll(mySocket)
assert.NoError(t, tmpErr) //nolint:testifylint // pre-existing issue from nektos/act
os.Unsetenv("DOCKER_HOST")
CommonSocketLocations = []string{mySocket}
defaultSocket, found := socketLocation()
// Act
ret, err := GetSocketAndHost("")
// Assert
assert.Equal(t, unixSocket, defaultSocket, "Expected default socket to match common socket location")
assert.True(t, found, "Expected default socket to be found")
assert.NoError(t, err, "Expected no error from GetSocketAndHost") //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, SocketAndHost{unixSocket, unixSocket}, ret, "Expected to match default socket location")
}
func TestGetSocketAndHostNoHostInvalidSocket(t *testing.T) {
// Arrange
isolateSocketEnv(t)
os.Unsetenv("DOCKER_HOST")
mySocket := "/my/socket/path.sock"
CommonSocketLocations = []string{"/unusual", "/socket", "/location"}
defaultSocket, found := socketLocation()
// Act
ret, err := GetSocketAndHost(mySocket)
// Assert
assert.False(t, found, "Expected no default socket to be found")
assert.Equal(t, "", defaultSocket, "Expected no default socket to be found") //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, SocketAndHost{}, ret, "Expected to match default socket location")
assert.Error(t, err, "Expected an error in invalid state")
}
func TestGetSocketAndHostOnlySocketValidButUnusualLocation(t *testing.T) {
// Arrange
isolateSocketEnv(t)
socketURI := "unix:///path/to/my.socket"
CommonSocketLocations = []string{"/unusual", "/location"}
os.Unsetenv("DOCKER_HOST")
defaultSocket, found := socketLocation()
// Act
ret, err := GetSocketAndHost(socketURI)
// Assert
// Default socket locations
assert.Equal(t, "", defaultSocket, "Expect default socket location to be empty") //nolint:testifylint // pre-existing issue from nektos/act
assert.False(t, found, "Expected no default socket to be found")
// Sane default
assert.NoError(t, err, "Expect no error from GetSocketAndHost") //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, socketURI, ret.Host, "Expect host to default to unusual socket")
}

View File

@@ -1,4 +1,8 @@
//go:build WITHOUT_DOCKER || !(linux || darwin || windows)
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2023 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build WITHOUT_DOCKER || !(linux || darwin || windows || netbsd)
package container
@@ -6,20 +10,21 @@ import (
"context"
"runtime"
"github.com/docker/docker/api/types"
"github.com/nektos/act/pkg/common"
"gitea.com/gitea/runner/act/common"
"github.com/moby/moby/api/types/system"
"github.com/pkg/errors"
)
// ImageExistsLocally returns a boolean indicating if an image with the
// requested name, tag and architecture exists in the local docker image store
func ImageExistsLocally(ctx context.Context, imageName string, platform string) (bool, error) {
func ImageExistsLocally(ctx context.Context, imageName, platform string) (bool, error) {
return false, errors.New("Unsupported Operation")
}
// RemoveImage removes image from local store, the function is used to run different
// container image architectures
func RemoveImage(ctx context.Context, imageName string, force bool, pruneChildren bool) (bool, error) {
func RemoveImage(ctx context.Context, imageName string, force, pruneChildren bool) (bool, error) {
return false, errors.New("Unsupported Operation")
}
@@ -46,8 +51,8 @@ func RunnerArch(ctx context.Context) string {
return runtime.GOOS
}
func GetHostInfo(ctx context.Context) (info types.Info, err error) {
return types.Info{}, nil
func GetHostInfo(ctx context.Context) (info system.Info, err error) {
return system.Info{}, nil
}
func NewDockerVolumeRemoveExecutor(volume string, force bool) common.Executor {

View File

@@ -1,13 +1,17 @@
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows))
// Copyright 2023 The Gitea Authors. All rights reserved.
// Copyright 2020 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !(WITHOUT_DOCKER || !(linux || darwin || windows || netbsd))
package container
import (
"context"
"github.com/docker/docker/api/types/filters"
"github.com/docker/docker/api/types/volume"
"github.com/nektos/act/pkg/common"
"gitea.com/gitea/runner/act/common"
"github.com/moby/moby/client"
)
func NewDockerVolumeRemoveExecutor(volumeName string, force bool) common.Executor {
@@ -18,12 +22,12 @@ func NewDockerVolumeRemoveExecutor(volumeName string, force bool) common.Executo
}
defer cli.Close()
list, err := cli.VolumeList(ctx, volume.ListOptions{Filters: filters.NewArgs()})
list, err := cli.VolumeList(ctx, client.VolumeListOptions{})
if err != nil {
return err
}
for _, vol := range list.Volumes {
for _, vol := range list.Items {
if vol.Name == volumeName {
return removeExecutor(volumeName, force)(ctx)
}
@@ -37,7 +41,7 @@ func NewDockerVolumeRemoveExecutor(volumeName string, force bool) common.Executo
func removeExecutor(volume string, force bool) common.Executor {
return func(ctx context.Context) error {
logger := common.Logger(ctx)
logger.Debugf("%sdocker volume rm %s", logPrefix, volume)
logger.Debugf("docker volume rm %s", volume)
if common.Dryrun(ctx) {
return nil
@@ -49,6 +53,7 @@ func removeExecutor(volume string, force bool) common.Executor {
}
defer cli.Close()
return cli.VolumeRemove(ctx, volume, force)
_, err = cli.VolumeRemove(ctx, volume, client.VolumeRemoveOptions{Force: force})
return err
}
}

View File

@@ -1,3 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2022 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import "context"
@@ -9,7 +13,7 @@ type ExecutionsEnvironment interface {
GetPathVariableName() string
DefaultPathVariable() string
JoinPathVariable(...string) string
GetRunnerContext(ctx context.Context) map[string]interface{}
GetRunnerContext(ctx context.Context) map[string]any
// On windows PATH and Path are the same key
IsEnvironmentCaseInsensitive() bool
}

View File

@@ -0,0 +1,27 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
"context"
"testing"
mobyclient "github.com/moby/moby/client"
)
// requireDocker skips the test unless a reachable docker daemon is available.
// GetDockerClient succeeds even without a running daemon (its ping is best-effort),
// so the daemon has to be pinged explicitly here to decide whether to skip.
func requireDocker(t *testing.T) {
t.Helper()
ctx := context.Background()
cli, err := GetDockerClient(ctx)
if err != nil {
t.Skipf("skipping: docker client unavailable: %v", err)
}
defer cli.Close()
if _, err := cli.Ping(ctx, mobyclient.PingOptions{}); err != nil {
t.Skipf("skipping: docker daemon unreachable: %v", err)
}
}

View File

@@ -1,3 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2022 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
@@ -13,15 +17,17 @@ import (
"path/filepath"
"runtime"
"strings"
"sync/atomic"
"time"
"gitea.com/gitea/runner/act/common"
"gitea.com/gitea/runner/act/filecollector"
"gitea.com/gitea/runner/act/lookpath"
"github.com/go-git/go-billy/v5/helper/polyfill"
"github.com/go-git/go-billy/v5/osfs"
"github.com/go-git/go-git/v5/plumbing/format/gitignore"
"golang.org/x/term"
"github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/lookpath"
)
type HostEnvironment struct {
@@ -29,12 +35,16 @@ type HostEnvironment struct {
TmpDir string
ToolCache string
Workdir string
ActPath string
CleanUp func()
StdOut io.Writer
// CleanWorkdir means teardown owns Workdir and may delete it. Leave false
// when Workdir points at a caller-owned checkout (e.g. `act` local mode).
CleanWorkdir bool
ActPath string
CleanUp func()
StdOut io.Writer
AllocatePTY bool // allocate a pseudo-TTY for each step's process
}
func (e *HostEnvironment) Create(_ []string, _ []string) common.Executor {
func (e *HostEnvironment) Create(_, _ []string) common.Executor {
return func(ctx context.Context) error {
return nil
}
@@ -71,7 +81,7 @@ func (e *HostEnvironment) CopyTarStream(ctx context.Context, destPath string, ta
return err
}
tr := tar.NewReader(tarStream)
cp := &copyCollector{
cp := &filecollector.CopyCollector{
DstDir: destPath,
}
for {
@@ -85,7 +95,7 @@ func (e *HostEnvironment) CopyTarStream(ctx context.Context, destPath string, ta
continue
}
if ctx.Err() != nil {
return fmt.Errorf("CopyTarStream has been cancelled")
return errors.New("CopyTarStream has been cancelled")
}
if err := cp.WriteFile(ti.Name, ti.FileInfo(), ti.Linkname, tr); err != nil {
return err
@@ -93,7 +103,7 @@ func (e *HostEnvironment) CopyTarStream(ctx context.Context, destPath string, ta
}
}
func (e *HostEnvironment) CopyDir(destPath string, srcPath string, useGitIgnore bool) common.Executor {
func (e *HostEnvironment) CopyDir(destPath, srcPath string, useGitIgnore bool) common.Executor {
return func(ctx context.Context) error {
logger := common.Logger(ctx)
srcPrefix := filepath.Dir(srcPath)
@@ -110,16 +120,16 @@ func (e *HostEnvironment) CopyDir(destPath string, srcPath string, useGitIgnore
ignorer = gitignore.NewMatcher(ps)
}
fc := &fileCollector{
Fs: &defaultFs{},
fc := &filecollector.FileCollector{
Fs: &filecollector.DefaultFs{},
Ignorer: ignorer,
SrcPath: srcPath,
SrcPrefix: srcPrefix,
Handler: &copyCollector{
Handler: &filecollector.CopyCollector{
DstDir: destPath,
},
}
return filepath.Walk(srcPath, fc.collectFiles(ctx, []string{}))
return filepath.Walk(srcPath, fc.CollectFiles(ctx, []string{}))
}
}
@@ -132,21 +142,21 @@ func (e *HostEnvironment) GetContainerArchive(ctx context.Context, srcPath strin
if err != nil {
return nil, err
}
tc := &tarCollector{
tc := &filecollector.TarCollector{
TarWriter: tw,
}
if fi.IsDir() {
srcPrefix := filepath.Dir(srcPath)
srcPrefix := srcPath
if !strings.HasSuffix(srcPrefix, string(filepath.Separator)) {
srcPrefix += string(filepath.Separator)
}
fc := &fileCollector{
Fs: &defaultFs{},
fc := &filecollector.FileCollector{
Fs: &filecollector.DefaultFs{},
SrcPath: srcPath,
SrcPrefix: srcPrefix,
Handler: tc,
}
err = filepath.Walk(srcPath, fc.collectFiles(ctx, []string{}))
err = filepath.Walk(srcPath, fc.CollectFiles(ctx, []string{}))
if err != nil {
return nil, err
}
@@ -187,12 +197,12 @@ func (e *HostEnvironment) Start(_ bool) common.Executor {
type ptyWriter struct {
Out io.Writer
AutoStop bool
AutoStop atomic.Bool
dirtyLine bool
}
func (w *ptyWriter) Write(buf []byte) (int, error) {
if w.AutoStop && len(buf) > 0 && buf[len(buf)-1] == 4 {
if w.AutoStop.Load() && len(buf) > 0 && buf[len(buf)-1] == 4 {
n, err := w.Out.Write(buf[:len(buf)-1])
if err != nil {
return n, err
@@ -226,7 +236,7 @@ func (l *localEnv) Getenv(name string) string {
func lookupPathHost(cmd string, env map[string]string, writer io.Writer) (string, error) {
f, err := lookpath.LookPath2(cmd, &localEnv{env: env})
if err != nil {
err := "Cannot find: " + fmt.Sprint(cmd) + " in PATH"
err := "Cannot find: " + cmd + " in PATH"
if _, _err := writer.Write([]byte(err + "\n")); _err != nil {
return "", fmt.Errorf("%v: %w", err, _err)
}
@@ -312,6 +322,30 @@ func (e *HostEnvironment) exec(ctx context.Context, command []string, cmdline st
cmd.Stderr = e.StdOut
cmd.Dir = wd
cmd.SysProcAttr = getSysProcAttr(cmdline, false)
// On Windows a step often launches a process tree (a shell that starts a
// child which spawns further GUI or background processes). The default
// context cancellation only kills the direct child, leaving the rest of the
// tree running; and because the orphans inherit cmd's stdout/stderr pipe,
// cmd.Wait() would block forever, hanging the runner. Kill the whole tree
// via a Job Object on cancellation, and bound the wait so a leftover pipe
// writer can never hang Wait indefinitely.
var killer atomic.Pointer[processKiller]
if runtime.GOOS == "windows" {
cmd.Cancel = func() error {
if k := killer.Load(); k != nil {
return k.Kill()
}
if cmd.Process != nil {
return cmd.Process.Kill()
}
return nil
}
// Once the step process has exited, give its I/O pipes at most this long
// to drain before Wait force-closes them and returns (Go's WaitDelay).
cmd.WaitDelay = 10 * time.Second
}
var ppty *os.File
var tty *os.File
defer func() {
@@ -322,36 +356,51 @@ func (e *HostEnvironment) exec(ctx context.Context, command []string, cmdline st
tty.Close()
}
}()
if true /* allocate Terminal */ {
if e.AllocatePTY {
var err error
ppty, tty, err = setupPty(cmd, cmdline)
if err != nil {
common.Logger(ctx).Debugf("Failed to setup Pty %v\n", err.Error())
}
}
writer := &ptyWriter{Out: e.StdOut}
logctx, finishLog := context.WithCancel(context.Background())
var writer *ptyWriter
var logctx context.Context
if ppty != nil {
writer = &ptyWriter{Out: e.StdOut}
var finishLog context.CancelFunc
logctx, finishLog = context.WithCancel(context.Background())
go copyPtyOutput(writer, ppty, finishLog)
} else {
finishLog()
}
if ppty != nil {
go writeKeepAlive(ppty)
}
err = cmd.Run()
if err := cmd.Start(); err != nil {
return err
}
if runtime.GOOS == "windows" {
// Assign the started process to a Job Object so cmd.Cancel can kill the
// whole descendant tree. Children spawned afterwards are auto-included.
// On failure (e.g. nested-job restrictions) we fall back to the default
// single-process kill; WaitDelay + end-of-job cleanup still apply.
if k, kerr := newProcessKiller(cmd.Process); kerr != nil {
common.Logger(ctx).Warnf("process tree kill setup failed, falling back to single-process kill: %v", kerr)
} else {
killer.Store(k)
defer k.Close()
}
}
err = cmd.Wait()
if err != nil {
var exitErr *exec.ExitError
if errors.As(err, &exitErr) {
return ExitCodeError(exitErr.ExitCode())
}
return err
}
if tty != nil {
writer.AutoStop = true
if _, err := tty.Write([]byte("\x04")); err != nil {
writer.AutoStop.Store(true)
if _, err := tty.WriteString("\x04"); err != nil {
common.Logger(ctx).Debug("Failed to write EOT")
}
}
<-logctx.Done()
if ppty != nil {
<-logctx.Done()
ppty.Close()
ppty = nil
}
@@ -380,12 +429,139 @@ func (e *HostEnvironment) UpdateFromEnv(srcPath string, env *map[string]string)
return parseEnvFile(e, srcPath, env)
}
func removePathWithRetry(ctx context.Context, path string) error {
if path == "" {
return nil
}
attempts := 1
delay := time.Duration(0)
if runtime.GOOS == "windows" {
attempts = 5
delay = 200 * time.Millisecond
}
var lastErr error
for i := 0; i < attempts; i++ {
if i > 0 {
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(delay):
}
}
lastErr = os.RemoveAll(path)
if lastErr == nil {
return nil
}
}
return lastErr
}
// buildWindowsWorkspaceKillScript builds a PowerShell command that `taskkill
// /T /F`s every process tree whose ExecutablePath or CommandLine references one
// of the given absolute workspace dirs, releasing file handles for cleanup.
//
// Win32_Process is used because it exposes both ExecutablePath and CommandLine
// (Get-Process doesn't, wmic is deprecated). Both match the dir+separator
// prefix, so a sibling dir sharing a name prefix (job1 vs job10) is spared.
// Ordinal String methods, not -like, so path metacharacters ([ ] ? *) stay
// literal.
//
// Pure function so the quote-escaping can be unit-tested without PowerShell.
func buildWindowsWorkspaceKillScript(dirs []string) string {
quoted := make([]string, len(dirs))
for i, d := range dirs {
// Single-quoted PowerShell literal; escape ' by doubling it.
quoted[i] = "'" + strings.ReplaceAll(d, "'", "''") + "'"
}
return `$paths = @(` + strings.Join(quoted, ",") + `)
$selfPid = $PID
Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {
if ($_.ProcessId -eq $selfPid) { return $false }
foreach ($p in $paths) {
$prefix = $p + '\'
if ($_.ExecutablePath -and $_.ExecutablePath.StartsWith($prefix, [System.StringComparison]::OrdinalIgnoreCase)) { return $true }
if ($_.CommandLine -and $_.CommandLine.IndexOf($prefix, [System.StringComparison]::OrdinalIgnoreCase) -ge 0) { return $true }
}
return $false
} | ForEach-Object {
& taskkill.exe /PID $_.ProcessId /T /F 2>$null | Out-Null
}
`
}
func (e *HostEnvironment) terminateRunningProcesses(ctx context.Context) {
if runtime.GOOS != "windows" {
return
}
// Detached: exec.CommandContext won't start on a cancelled ctx, and a
// server cancel has already cancelled the parent ctx.
killCtx, killCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer killCancel()
logger := common.Logger(ctx)
// Workspace dirs we own. Any process running from or referencing one is a
// leftover job process. ToolCache is shared across jobs; Workdir only when
// we own it (else it's a caller-provided checkout, e.g. act local mode).
owned := []string{e.Path, e.TmpDir}
if e.CleanWorkdir {
owned = append(owned, e.Workdir)
}
dirs := make([]string, 0, len(owned))
for _, d := range owned {
if d == "" {
continue
}
abs, err := filepath.Abs(d)
if err != nil {
continue
}
dirs = append(dirs, abs)
}
if len(dirs) == 0 {
return
}
script := buildWindowsWorkspaceKillScript(dirs)
cmd := exec.CommandContext(killCtx, "powershell.exe", "-NoProfile", "-NonInteractive", "-Command", script)
out, err := cmd.CombinedOutput()
if err != nil {
logger.Debugf("workspace process-tree kill via PowerShell failed: %v output=%s", err, strings.TrimSpace(string(out)))
}
}
func (e *HostEnvironment) Remove() common.Executor {
return func(ctx context.Context) error {
// Ensure any lingering child processes are ended before attempting
// to remove the workspace (Windows file locks otherwise prevent cleanup).
e.terminateRunningProcesses(ctx)
// Only removes per-job misc state. Must not remove the cache/toolcache root.
if e.CleanUp != nil {
e.CleanUp()
}
return os.RemoveAll(e.Path)
// Detach: a cancelled ctx would skip removePathWithRetry's retries,
// which absorb Windows file-handle release lag after the kill above.
rmCtx, rmCancel := context.WithTimeout(context.Background(), 30*time.Second)
defer rmCancel()
logger := common.Logger(ctx)
var errs []error
if err := removePathWithRetry(rmCtx, e.Path); err != nil {
logger.Warnf("failed to remove host misc state %s: %v", e.Path, err)
errs = append(errs, err)
}
if e.CleanWorkdir {
if err := removePathWithRetry(rmCtx, e.Workdir); err != nil {
logger.Warnf("failed to remove host workspace %s: %v", e.Workdir, err)
errs = append(errs, err)
}
}
return errors.Join(errs...)
}
}
@@ -407,9 +583,10 @@ func (e *HostEnvironment) GetActPath() string {
}
func (*HostEnvironment) GetPathVariableName() string {
if runtime.GOOS == "plan9" {
switch runtime.GOOS {
case "plan9":
return "path"
} else if runtime.GOOS == "windows" {
case "windows":
return "Path" // Actually we need a case insensitive map
}
return "PATH"
@@ -448,8 +625,8 @@ func goOsToActionOs(os string) string {
return os
}
func (e *HostEnvironment) GetRunnerContext(_ context.Context) map[string]interface{} {
return map[string]interface{}{
func (e *HostEnvironment) GetRunnerContext(_ context.Context) map[string]any {
return map[string]any{
"os": goOsToActionOs(runtime.GOOS),
"arch": goArchToActionArch(runtime.GOARCH),
"temp": e.TmpDir,
@@ -457,7 +634,7 @@ func (e *HostEnvironment) GetRunnerContext(_ context.Context) map[string]interfa
}
}
func (e *HostEnvironment) ReplaceLogWriter(stdout io.Writer, _ io.Writer) (io.Writer, io.Writer) {
func (e *HostEnvironment) ReplaceLogWriter(stdout, _ io.Writer) (io.Writer, io.Writer) {
org := e.StdOut
e.StdOut = stdout
return org, org

View File

@@ -0,0 +1,250 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2022 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
"archive/tar"
"bytes"
"context"
"io"
"os"
"path"
"path/filepath"
"runtime"
"strings"
"testing"
"gitea.com/gitea/runner/act/common"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// Type assert HostEnvironment implements ExecutionsEnvironment
var _ ExecutionsEnvironment = &HostEnvironment{}
func TestCopyDir(t *testing.T) {
dir := t.TempDir()
ctx := context.Background()
e := &HostEnvironment{
Path: filepath.Join(dir, "path"),
TmpDir: filepath.Join(dir, "tmp"),
ToolCache: filepath.Join(dir, "tool_cache"),
ActPath: filepath.Join(dir, "act_path"),
StdOut: os.Stdout,
Workdir: path.Join("testdata", "scratch"),
}
_ = os.MkdirAll(e.Path, 0o700)
_ = os.MkdirAll(e.TmpDir, 0o700)
_ = os.MkdirAll(e.ToolCache, 0o700)
_ = os.MkdirAll(e.ActPath, 0o700)
err := e.CopyDir(e.Workdir, e.Path, true)(ctx)
assert.NoError(t, err)
}
func TestGetContainerArchive(t *testing.T) {
dir := t.TempDir()
ctx := context.Background()
e := &HostEnvironment{
Path: filepath.Join(dir, "path"),
TmpDir: filepath.Join(dir, "tmp"),
ToolCache: filepath.Join(dir, "tool_cache"),
ActPath: filepath.Join(dir, "act_path"),
StdOut: os.Stdout,
Workdir: path.Join("testdata", "scratch"),
}
_ = os.MkdirAll(e.Path, 0o700)
_ = os.MkdirAll(e.TmpDir, 0o700)
_ = os.MkdirAll(e.ToolCache, 0o700)
_ = os.MkdirAll(e.ActPath, 0o700)
expectedContent := []byte("sdde/7sh")
err := os.WriteFile(filepath.Join(e.Path, "action.yml"), expectedContent, 0o600)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
archive, err := e.GetContainerArchive(ctx, e.Path)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
defer archive.Close()
reader := tar.NewReader(archive)
h, err := reader.Next()
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, "action.yml", h.Name)
content, err := io.ReadAll(reader)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, expectedContent, content)
_, err = reader.Next()
assert.ErrorIs(t, err, io.EOF)
}
func TestHostEnvironmentExecExitCode(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("uses POSIX shell")
}
dir := t.TempDir()
ctx := context.Background()
e := &HostEnvironment{
Path: filepath.Join(dir, "path"),
TmpDir: filepath.Join(dir, "tmp"),
ToolCache: filepath.Join(dir, "tool_cache"),
ActPath: filepath.Join(dir, "act_path"),
StdOut: io.Discard,
Workdir: filepath.Join(dir, "path"),
}
for _, p := range []string{e.Path, e.TmpDir, e.ToolCache, e.ActPath} {
assert.NoError(t, os.MkdirAll(p, 0o700)) //nolint:testifylint // test setup
}
err := e.Exec([]string{"sh", "-c", "exit 3"}, map[string]string{"PATH": os.Getenv("PATH")}, "", "")(ctx)
var exitErr ExitCodeError
require.ErrorAs(t, err, &exitErr)
assert.Equal(t, ExitCodeError(3), exitErr)
assert.Equal(t, "Process completed with exit code 3.", err.Error())
}
func TestHostEnvironmentAllocatePTY(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip("uses POSIX shell")
}
for _, tc := range []struct {
name string
allocPTY bool
expect string
}{
{name: "off", allocPTY: false, expect: "NOTTY"},
{name: "on", allocPTY: true, expect: "TTY"},
} {
t.Run(tc.name, func(t *testing.T) {
dir := t.TempDir()
buf := &bytes.Buffer{}
e := &HostEnvironment{
Path: filepath.Join(dir, "path"),
TmpDir: filepath.Join(dir, "tmp"),
ToolCache: filepath.Join(dir, "tool_cache"),
ActPath: filepath.Join(dir, "act_path"),
StdOut: buf,
Workdir: filepath.Join(dir, "path"),
AllocatePTY: tc.allocPTY,
}
for _, p := range []string{e.Path, e.TmpDir, e.ToolCache, e.ActPath} {
require.NoError(t, os.MkdirAll(p, 0o700))
}
err := e.Exec(
[]string{"sh", "-c", "[ -t 1 ] && printf TTY || printf NOTTY"},
map[string]string{"PATH": os.Getenv("PATH")}, "", "",
)(context.Background())
require.NoError(t, err)
got := strings.TrimSpace(strings.ReplaceAll(buf.String(), "\r", ""))
assert.Equal(t, tc.expect, got)
})
}
}
func TestHostEnvironmentRemovePreservesWorkdirByDefault(t *testing.T) {
logger := logrus.New()
ctx := common.WithLogger(context.Background(), logrus.NewEntry(logger))
base := t.TempDir()
miscRoot := filepath.Join(base, "misc")
path := filepath.Join(miscRoot, "hostexecutor")
require.NoError(t, os.MkdirAll(path, 0o700))
workdir := filepath.Join(base, "workspace", "owner", "repo")
require.NoError(t, os.MkdirAll(workdir, 0o700))
e := &HostEnvironment{
Path: path,
Workdir: workdir,
CleanUp: func() {
_ = os.RemoveAll(miscRoot)
},
StdOut: os.Stdout,
}
require.NoError(t, e.Remove()(ctx))
_, err := os.Stat(workdir)
require.NoError(t, err)
}
func TestHostEnvironmentRemoveCleansWorkdirWhenOwned(t *testing.T) {
logger := logrus.New()
ctx := common.WithLogger(context.Background(), logrus.NewEntry(logger))
base := t.TempDir()
miscRoot := filepath.Join(base, "misc")
path := filepath.Join(miscRoot, "hostexecutor")
require.NoError(t, os.MkdirAll(path, 0o700))
workdir := filepath.Join(base, "workspace", "123", "owner", "repo")
require.NoError(t, os.MkdirAll(workdir, 0o700))
e := &HostEnvironment{
Path: path,
Workdir: workdir,
CleanWorkdir: true,
CleanUp: func() {
_ = os.RemoveAll(miscRoot)
},
StdOut: os.Stdout,
}
require.NoError(t, e.Remove()(ctx))
_, err := os.Stat(workdir)
assert.ErrorIs(t, err, os.ErrNotExist)
}
func TestBuildWindowsWorkspaceKillScript(t *testing.T) {
t.Run("single dir", func(t *testing.T) {
s := buildWindowsWorkspaceKillScript([]string{`C:\workspace\job1`})
assert.Contains(t, s, `$paths = @('C:\workspace\job1')`)
// Self-PID guard is essential — without it the script could taskkill
// the PowerShell process running it.
assert.Contains(t, s, "$selfPid = $PID")
assert.Contains(t, s, "$_.ProcessId -eq $selfPid")
// Must match both ExecutablePath (binaries from the workspace) and
// CommandLine (system binaries invoked with workspace paths in args),
// both bounded by dir+separator so a name-prefix sibling is spared.
assert.Contains(t, s, `$prefix = $p + '\'`)
assert.Contains(t, s, "$_.ExecutablePath.StartsWith($prefix")
assert.Contains(t, s, "$_.CommandLine.IndexOf($prefix")
// Each matched PID must be tree-killed, not just stopped.
assert.Contains(t, s, "taskkill.exe /PID $_.ProcessId /T /F")
})
t.Run("multiple dirs comma-separated", func(t *testing.T) {
s := buildWindowsWorkspaceKillScript([]string{
`C:\work\path`,
`C:\work\workdir`,
`C:\Users\runner\AppData\Local\Temp\job-42`,
})
assert.Contains(t, s, `'C:\work\path'`)
assert.Contains(t, s, `'C:\work\workdir'`)
assert.Contains(t, s, `'C:\Users\runner\AppData\Local\Temp\job-42'`)
// Commas between entries — no trailing comma, no leading comma.
assert.Contains(t, s, `'C:\work\path','C:\work\workdir',`)
})
t.Run("path with single quote is escaped", func(t *testing.T) {
// In PowerShell single-quoted strings the only special char is the
// quote itself, escaped by doubling. A workspace path that ever
// contained `'` would inject a command into the script otherwise.
s := buildWindowsWorkspaceKillScript([]string{`C:\work\it's\path`})
assert.Contains(t, s, `'C:\work\it''s\path'`)
// And it must NOT appear unescaped — otherwise the quote would
// terminate the literal early.
assert.NotContains(t, s, `'C:\work\it's\path'`)
})
t.Run("path with wildcard metacharacters is matched literally", func(t *testing.T) {
// A path containing [ ] ? * must be embedded verbatim and matched with
// ordinal String methods, not -like, otherwise the metacharacters would
// be interpreted as wildcards and the leftover process could escape.
s := buildWindowsWorkspaceKillScript([]string{`C:\work\[job]?1`})
assert.Contains(t, s, `'C:\work\[job]?1'`)
assert.NotContains(t, s, "-like")
assert.Contains(t, s, "StartsWith")
assert.Contains(t, s, "IndexOf")
})
t.Run("empty dir list still produces a valid script", func(t *testing.T) {
s := buildWindowsWorkspaceKillScript(nil)
// Empty array literal — script runs, matches nothing, is a no-op.
assert.Contains(t, s, "$paths = @()")
assert.Contains(t, s, "Get-CimInstance Win32_Process")
})
}

View File

@@ -1,3 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2022 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
@@ -10,8 +14,7 @@ import (
log "github.com/sirupsen/logrus"
)
type LinuxContainerEnvironmentExtensions struct {
}
type LinuxContainerEnvironmentExtensions struct{}
// Resolves the equivalent host path inside the container
// This is required for windows and WSL 2 to translate things like C:\Users\Myproject to /mnt/users/Myproject
@@ -63,8 +66,8 @@ func (*LinuxContainerEnvironmentExtensions) JoinPathVariable(paths ...string) st
return strings.Join(paths, ":")
}
func (*LinuxContainerEnvironmentExtensions) GetRunnerContext(ctx context.Context) map[string]interface{} {
return map[string]interface{}{
func (*LinuxContainerEnvironmentExtensions) GetRunnerContext(ctx context.Context) map[string]any {
return map[string]any{
"os": "Linux",
"arch": RunnerArch(ctx),
"temp": "/tmp",

View File

@@ -1,3 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2022 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
@@ -31,22 +35,17 @@ func TestContainerPath(t *testing.T) {
for _, v := range []containerPathJob{
{"/mnt/c/Users/act/go/src/github.com/nektos/act", "C:\\Users\\act\\go\\src\\github.com\\nektos\\act\\", ""},
{"/mnt/f/work/dir", `F:\work\dir`, ""},
{"/mnt/c/windows/to/unix", "windows\\to\\unix", fmt.Sprintf("%s\\", rootDrive)},
{fmt.Sprintf("/mnt/%v/act", rootDriveLetter), "act", fmt.Sprintf("%s\\", rootDrive)},
{"/mnt/c/windows/to/unix", "windows\\to\\unix", rootDrive + "\\"},
{fmt.Sprintf("/mnt/%v/act", rootDriveLetter), "act", rootDrive + "\\"},
} {
if v.workDir != "" {
if err := os.Chdir(v.workDir); err != nil {
log.Error(err)
t.Fail()
}
t.Chdir(v.workDir)
}
assert.Equal(t, v.destinationPath, linuxcontainerext.ToContainerPath(v.sourcePath))
}
if err := os.Chdir(cwd); err != nil {
log.Error(err)
}
t.Chdir(cwd)
} else {
cwd, err := os.Getwd()
if err != nil {

View File

@@ -1,3 +1,7 @@
// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2022 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
@@ -8,7 +12,7 @@ import (
"io"
"strings"
"github.com/nektos/act/pkg/common"
"gitea.com/gitea/runner/act/common"
)
func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Executor {
@@ -25,6 +29,8 @@ func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Ex
return err
}
s := bufio.NewScanner(reader)
// Default 64 KiB max token size is too small for realistic env-file lines; allow up to 16 MiB.
s.Buffer(make([]byte, 0, 64*1024), 16*1024*1024)
for s.Scan() {
line := s.Text()
singleLineEnv := strings.Index(line, "=")
@@ -46,6 +52,9 @@ func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Ex
}
multiLineEnvContent += content
}
if err := s.Err(); err != nil {
return fmt.Errorf("reading env file: %w", err)
}
if !delimiterFound {
return fmt.Errorf("invalid format delimiter '%v' not found before end of file", multiLineEnvDelimiter)
}
@@ -54,6 +63,9 @@ func parseEnvFile(e Container, srcPath string, env *map[string]string) common.Ex
return fmt.Errorf("invalid format '%v', expected a line with '=' or '<<'", line)
}
}
if err := s.Err(); err != nil {
return fmt.Errorf("reading env file: %w", err)
}
env = &localEnv
return nil
}

View File

@@ -0,0 +1,75 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
"bufio"
"context"
"os"
"path/filepath"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func newTestHostEnv(t *testing.T) (*HostEnvironment, string) {
t.Helper()
e := &HostEnvironment{Path: t.TempDir()}
return e, filepath.Join(e.Path, "envfile")
}
func TestParseEnvFileSingleLine(t *testing.T) {
e, envPath := newTestHostEnv(t)
require.NoError(t, os.WriteFile(envPath, []byte("FOO=bar\nBAZ=qux\n"), 0o600))
env := map[string]string{}
require.NoError(t, parseEnvFile(e, envPath, &env)(context.Background()))
assert.Equal(t, "bar", env["FOO"])
assert.Equal(t, "qux", env["BAZ"])
}
func TestParseEnvFileMultiLine(t *testing.T) {
e, envPath := newTestHostEnv(t)
content := "FOO<<EOF\nline1\nline2\nEOF\n"
require.NoError(t, os.WriteFile(envPath, []byte(content), 0o600))
env := map[string]string{}
require.NoError(t, parseEnvFile(e, envPath, &env)(context.Background()))
assert.Equal(t, "line1\nline2", env["FOO"])
}
func TestParseEnvFileLargeValueWithinLimit(t *testing.T) {
e, envPath := newTestHostEnv(t)
big := strings.Repeat("x", 2*1024*1024)
content := "FOO<<EOF\n" + big + "\nEOF\n"
require.NoError(t, os.WriteFile(envPath, []byte(content), 0o600))
env := map[string]string{}
require.NoError(t, parseEnvFile(e, envPath, &env)(context.Background()))
assert.Equal(t, big, env["FOO"])
}
func TestParseEnvFileLineExceedsBufferReportsScannerError(t *testing.T) {
e, envPath := newTestHostEnv(t)
tooBig := strings.Repeat("x", 17*1024*1024) // over the 16 MiB cap
content := "FOO<<EOF\n" + tooBig + "\nEOF\n"
require.NoError(t, os.WriteFile(envPath, []byte(content), 0o600))
env := map[string]string{}
err := parseEnvFile(e, envPath, &env)(context.Background())
require.ErrorIs(t, err, bufio.ErrTooLong)
assert.Contains(t, err.Error(), "reading env file")
}
func TestParseEnvFileMissingDelimiter(t *testing.T) {
e, envPath := newTestHostEnv(t)
require.NoError(t, os.WriteFile(envPath, []byte("FOO<<EOF\nline1\nline2\n"), 0o600))
env := map[string]string{}
err := parseEnvFile(e, envPath, &env)(context.Background())
require.Error(t, err)
assert.Contains(t, err.Error(), "delimiter")
}

View File

@@ -0,0 +1,19 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
//go:build !windows
package container
import "os"
// processKiller is a no-op on non-Windows platforms. The Job Object based
// tree-kill is only wired in on Windows (see exec()); elsewhere the default
// exec.CommandContext cancellation and Setpgid handling apply.
type processKiller struct{}
func newProcessKiller(_ *os.Process) (*processKiller, error) { return &processKiller{}, nil }
func (k *processKiller) Kill() error { return nil }
func (k *processKiller) Close() error { return nil }

View File

@@ -0,0 +1,71 @@
// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package container
import (
"os"
"golang.org/x/sys/windows"
)
// processKiller terminates a step process together with its entire descendant
// tree via a Windows Job Object.
//
// Background: a step often launches a process tree (a shell that starts a
// child which in turn spawns further GUI or background processes). The default
// exec.CommandContext cancellation only kills the direct child, so cancelling a
// job left the rest of the tree running. Because those orphans inherited the
// step's stdout/stderr pipe, cmd.Wait() also blocked forever and the runner hung.
//
// Assigning the step process to a Job Object lets us kill the whole tree
// atomically on cancellation (TerminateJobObject), which also closes the
// inherited pipe handles so cmd.Wait() can return.
type processKiller struct {
job windows.Handle
}
// newProcessKiller creates a Job Object and assigns p (an already-started
// process) to it. Children spawned by p afterwards are automatically part of
// the job. The job does NOT use JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, so closing
// the handle on normal completion does not kill legitimate background
// processes; the tree is only torn down by an explicit Kill (cancellation).
func newProcessKiller(p *os.Process) (*processKiller, error) {
job, err := windows.CreateJobObject(nil, nil)
if err != nil {
return nil, err
}
h, err := windows.OpenProcess(windows.PROCESS_SET_QUOTA|windows.PROCESS_TERMINATE, false, uint32(p.Pid))
if err != nil {
windows.CloseHandle(job)
return nil, err
}
defer windows.CloseHandle(h)
if err := windows.AssignProcessToJobObject(job, h); err != nil {
windows.CloseHandle(job)
return nil, err
}
return &processKiller{job: job}, nil
}
// Kill terminates every process currently assigned to the job (the step process
// and all of its descendants).
func (k *processKiller) Kill() error {
if k == nil || k.job == 0 {
return nil
}
return windows.TerminateJobObject(k.job, 1)
}
// Close releases the job handle. It does not terminate the processes.
func (k *processKiller) Close() error {
if k == nil || k.job == 0 {
return nil
}
h := k.job
k.job = 0
return windows.CloseHandle(h)
}

Some files were not shown because too many files have changed in this diff Show More