Skip to content

Add Actions API endpoints for workflow run management and logs#35382

Open
rossigee wants to merge 10 commits intogo-gitea:mainfrom
rossigee:feature/runner-logs-api-endpoint
Open

Add Actions API endpoints for workflow run management and logs#35382
rossigee wants to merge 10 commits intogo-gitea:mainfrom
rossigee:feature/runner-logs-api-endpoint

Conversation

@rossigee
Copy link
Copy Markdown
Contributor

@rossigee rossigee commented Aug 29, 2025

Implements REST API endpoints for GitHub Actions workflow run management and logs:

  • POST /actions/runs/{run}/cancel - Cancel running workflow
  • POST /actions/runs/{run}/approve - Approve workflow requiring approval
  • GET /actions/runs/{run}/logs - Download all run logs as archive
  • GET /actions/runs/{run}/jobs/{job_id}/logs - Download job logs

Features:

  • Proper permission checks and workflow validation
  • Job dependency handling for reruns (via existing rerun endpoints)
  • Comprehensive test coverage

Note: Uses existing implementations for rerun endpoints from action.go.

Fixes workflow management gaps in API compatibility.

@GiteaBot GiteaBot added the lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. label Aug 29, 2025
@rossigee
Copy link
Copy Markdown
Contributor Author

Addresses #35176

@ChristopherHX
Copy link
Copy Markdown
Contributor

Please be aware existing Gitea workflow api do not use runindex, {run} is expected to be the run id.

also jobs use jobid instead of jobindex.


GET /actions/runs/{run}/jobs/{job_id}/logs - Download job logs
is already implemented

m.Get("/{job_id}/logs", repo.DownloadActionsRunJobLogs)

@rossigee rossigee force-pushed the feature/runner-logs-api-endpoint branch from d8ca88a to f2cf2e6 Compare September 18, 2025 13:17
@rossigee
Copy link
Copy Markdown
Contributor Author

Comments addressed. Please re-review.

@lunny
Copy link
Copy Markdown
Member

lunny commented Sep 19, 2025

CI failure is still related.

@ChristopherHX
Copy link
Copy Markdown
Contributor

You seem to have messed up the PR diff, by not doing a merge. This is hard to read right now so I merge the branch via GitHub UI first

Copy link
Copy Markdown
Contributor

@ChristopherHX ChristopherHX left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to look later again, your added or existing tests are at least failing that needs to be resolved.

Comment thread routers/api/v1/api.go
Comment thread routers/api/v1/api.go Outdated
@ChristopherHX
Copy link
Copy Markdown
Contributor

ChristopherHX commented Sep 25, 2025

You might be using a wrong user account for tests, do they pass locally?

--- FAIL: TestAPIActionsRerunWorkflowRun (0.44s)
    testlogger.go:61: 2025/09/25 11:46:04 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/gitea-lfs-meta
    testlogger.go:61: 2025/09/25 11:46:04 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/testdata/data/artifacts
    testlogger.go:61: 2025/09/25 11:46:04 HTTPRequest [I] router: completed GET /user/login for test-mock:12345, 200 OK in 2.1ms @ auth/auth.go:184(auth.SignIn)
    testlogger.go:61: 2025/09/25 11:46:04 HTTPRequest [I] router: completed POST /user/login for test-mock:12345, 303 See Other in 5.9ms @ auth/auth.go:197(auth.SignInPost)
    testlogger.go:61: 2025/09/25 11:46:04 HTTPRequest [I] router: completed POST /user/settings/applications for test-mock:12345, 303 See Other in 6.0ms @ setting/applications.go:38(setting.ApplicationsPost)
    api_actions_run_test.go:150: Response:  {"message":"User does not have write access to actions","url":"http://localhost:3003/api/swagger"}
        
    api_actions_run_test.go:150: 
        	Error Trace:	/home/runner/work/gitea/gitea/tests/integration/integration_test.go:354
        	            				/home/runner/work/gitea/gitea/tests/integration/api_actions_run_test.go:150
        	Error:      	Not equal: 
        	            	expected: 200
        	            	actual  : 403
        	Test:       	TestAPIActionsRerunWorkflowRun
        	Messages:   	Request: POST /api/v1/repos/user2/repo2/actions/runs/795/rerun
    testlogger.go:61: 2025/09/25 11:46:04 HTTPRequest [I] router: completed POST /api/v1/repos/user2/repo2/actions/runs/795/rerun for test-mock:12345, 403 Forbidden in 7.0ms @ repo/actions_run.go:78(repo.RerunWorkflowRun)
=== TestAPIActionsRerunWorkflowRunPermissions (tests/integration/api_actions_run_test.go:164)
--- FAIL: TestAPIActionsRerunWorkflowRunPermissions (0.44s)
    testlogger.go:61: 2025/09/25 11:46:04 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/gitea-lfs-meta
    testlogger.go:61: 2025/09/25 11:46:04 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/testdata/data/artifacts
    testlogger.go:61: 2025/09/25 11:46:04 HTTPRequest [I] router: completed GET /user/login for test-mock:12345, 200 OK in 2.1ms @ auth/auth.go:184(auth.SignIn)
    testlogger.go:61: 2025/09/25 11:46:04 HTTPRequest [I] router: completed POST /user/login for test-mock:12345, 303 See Other in 5.8ms @ auth/auth.go:197(auth.SignInPost)
    testlogger.go:61: 2025/09/25 11:46:04 HTTPRequest [I] router: completed POST /user/settings/applications for test-mock:12345, 303 See Other in 6.1ms @ setting/applications.go:38(setting.ApplicationsPost)
    api_actions_run_test.go:174: Response:  {"errors":null,"message":"not found","url":"http://localhost:3003/api/swagger"}
        
    testlogger.go:61: 2025/09/25 11:46:04 HTTPRequest [I] router: completed POST /api/v1/repos/user2/repo2/actions/runs/795/rerun for test-mock:12345, 404 Not Found in 8.4ms @ v1/api.go:138(v1.Routes.func2.(*Router).Group.Routes.func2.8.repoAssignment.93)
    api_actions_run_test.go:174: 
        	Error Trace:	/home/runner/work/gitea/gitea/tests/integration/integration_test.go:354
        	            				/home/runner/work/gitea/gitea/tests/integration/api_actions_run_test.go:174
        	Error:      	Not equal: 
        	            	expected: 403
        	            	actual  : 404
        	Test:       	TestAPIActionsRerunWorkflowRunPermissions
        	Messages:   	Request: POST /api/v1/repos/user2/repo2/actions/runs/795/rerun
=== TestAPIActionsCancelWorkflowRun (tests/integration/api_actions_run_test.go:178)
--- FAIL: TestAPIActionsCancelWorkflowRun (0.45s)
    testlogger.go:61: 2025/09/25 11:46:04 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/gitea-lfs-meta
    testlogger.go:61: 2025/09/25 11:46:05 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/testdata/data/artifacts
    testlogger.go:61: 2025/09/25 11:46:05 HTTPRequest [I] router: completed GET /user/login for test-mock:12345, 200 OK in 2.1ms @ auth/auth.go:184(auth.SignIn)
    testlogger.go:61: 2025/09/25 11:46:05 HTTPRequest [I] router: completed POST /user/login for test-mock:12345, 303 See Other in 5.8ms @ auth/auth.go:197(auth.SignInPost)
    testlogger.go:61: 2025/09/25 11:46:05 HTTPRequest [I] router: completed POST /user/settings/applications for test-mock:12345, 303 See Other in 5.9ms @ setting/applications.go:38(setting.ApplicationsPost)
    testlogger.go:61: 2025/09/25 11:46:05 HTTPRequest [I] router: completed POST /api/v1/repos/user5/repo4/actions/runs/793/cancel for test-mock:12345, 403 Forbidden in 9.3ms @ repo/actions_run.go:163(repo.CancelWorkflowRun)
    api_actions_run_test.go:188: Response:  {"message":"User does not have write access to actions","url":"http://localhost:3003/api/swagger"}
        
    api_actions_run_test.go:188: 
        	Error Trace:	/home/runner/work/gitea/gitea/tests/integration/integration_test.go:354
        	            				/home/runner/work/gitea/gitea/tests/integration/api_actions_run_test.go:188
        	Error:      	Not equal: 
        	            	expected: 200
        	            	actual  : 403
        	Test:       	TestAPIActionsCancelWorkflowRun
        	Messages:   	Request: POST /api/v1/repos/user5/repo4/actions/runs/793/cancel
=== TestAPIActionsCancelWorkflowRunPermissions (tests/integration/api_actions_run_test.go:197)
=== TestAPIActionsApproveWorkflowRun (tests/integration/api_actions_run_test.go:211)
--- FAIL: TestAPIActionsApproveWorkflowRun (0.44s)
    testlogger.go:61: 2025/09/25 11:46:05 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/gitea-lfs-meta
    testlogger.go:61: 2025/09/25 11:46:05 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/testdata/data/artifacts
    testlogger.go:61: 2025/09/25 11:46:05 HTTPRequest [I] router: completed GET /user/login for test-mock:12345, 200 OK in 2.0ms @ auth/auth.go:184(auth.SignIn)
    testlogger.go:61: 2025/09/25 11:46:05 HTTPRequest [I] router: completed POST /user/login for test-mock:12345, 303 See Other in 7.4ms @ auth/auth.go:197(auth.SignInPost)
    testlogger.go:61: 2025/09/25 11:46:05 HTTPRequest [I] router: completed POST /user/settings/applications for test-mock:12345, 303 See Other in 6.4ms @ setting/applications.go:38(setting.ApplicationsPost)
    api_actions_run_test.go:221: Response:  {"message":"User does not have write access to actions","url":"http://localhost:3003/api/swagger"}
        
    api_actions_run_test.go:221: 
        	Error Trace:	/home/runner/work/gitea/gitea/tests/integration/integration_test.go:354
        	            				/home/runner/work/gitea/gitea/tests/integration/api_actions_run_test.go:221
        	Error:      	Not equal: 
        	            	expected: 200
        	            	actual  : 403
        	Test:       	TestAPIActionsApproveWorkflowRun
        	Messages:   	Request: POST /api/v1/repos/user2/repo2/actions/runs/795/approve
    testlogger.go:61: 2025/09/25 11:46:05 HTTPRequest [I] router: completed POST /api/v1/repos/user2/repo2/actions/runs/795/approve for test-mock:12345, 403 Forbidden in 7.6ms @ repo/actions_run.go:264(repo.ApproveWorkflowRun)
=== TestAPIActionsRerunWorkflowJob (tests/integration/api_actions_run_test.go:230)
--- FAIL: TestAPIActionsRerunWorkflowJob (0.45s)
    testlogger.go:61: 2025/09/25 11:46:06 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/gitea-lfs-meta
    testlogger.go:61: 2025/09/25 11:46:06 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/testdata/data/artifacts
    testlogger.go:61: 2025/09/25 11:46:06 HTTPRequest [I] router: completed GET /user/login for test-mock:12345, 200 OK in 2.2ms @ auth/auth.go:184(auth.SignIn)
    testlogger.go:61: 2025/09/25 11:46:06 HTTPRequest [I] router: completed POST /user/login for test-mock:12345, 303 See Other in 7.5ms @ auth/auth.go:197(auth.SignInPost)
    testlogger.go:61: 2025/09/25 11:46:06 HTTPRequest [I] router: completed POST /user/settings/applications for test-mock:12345, 303 See Other in 5.9ms @ setting/applications.go:38(setting.ApplicationsPost)
    api_actions_run_test.go:240: Response:  {"message":"User does not have write access to actions","url":"http://localhost:3003/api/swagger"}
        
    api_actions_run_test.go:240: 
        	Error Trace:	/home/runner/work/gitea/gitea/tests/integration/integration_test.go:354
        	            				/home/runner/work/gitea/gitea/tests/integration/api_actions_run_test.go:240
        	Error:      	Not equal: 
        	            	expected: 200
        	            	actual  : 403
        	Test:       	TestAPIActionsRerunWorkflowJob
        	Messages:   	Request: POST /api/v1/repos/user2/repo2/actions/runs/795/jobs/192/rerun
    testlogger.go:61: 2025/09/25 11:46:06 HTTPRequest [I] router: completed POST /api/v1/repos/user2/repo2/actions/runs/795/jobs/192/rerun for test-mock:12345, 403 Forbidden in 9.0ms @ repo/actions_run.go:362(repo.RerunWorkflowJob)
=== TestAPIActionsGetWorkflowRunLogs (tests/integration/api_actions_run_test.go:249)
=== TestAPIActionsGetWorkflowJobLogs (tests/integration/api_actions_run_test.go:268)
--- FAIL: TestAPIActionsGetWorkflowJobLogs (0.44s)
    testlogger.go:61: 2025/09/25 11:46:07 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/gitea-lfs-meta
    testlogger.go:61: 2025/09/25 11:46:07 modules/storage/local.go:33:NewLocalStorage() [I] Creating new Local Storage at /home/runner/work/gitea/gitea/tests/testdata/data/artifacts
    testlogger.go:61: 2025/09/25 11:46:07 HTTPRequest [I] router: completed GET /user/login for test-mock:12345, 200 OK in 2.2ms @ auth/auth.go:184(auth.SignIn)
    testlogger.go:61: 2025/09/25 11:46:07 HTTPRequest [I] router: completed POST /user/login for test-mock:12345, 303 See Other in 6.2ms @ auth/auth.go:197(auth.SignInPost)
    testlogger.go:61: 2025/09/25 11:46:07 HTTPRequest [I] router: completed POST /user/settings/applications for test-mock:12345, 303 See Other in 5.9ms @ setting/applications.go:38(setting.ApplicationsPost)
    api_actions_run_test.go:278: Response:  {"message":"Job not found in this run","url":"http://localhost:3003/api/swagger"}
        
    testlogger.go:61: 2025/09/25 11:46:07 HTTPRequest [I] router: completed GET /api/v1/repos/user2/repo2/actions/runs/795/jobs/192/logs for test-mock:12345, 404 Not Found in 8.0ms @ repo/actions_run.go:640(repo.GetWorkflowJobLogs)
    api_actions_run_test.go:278: 
        	Error Trace:	/home/runner/work/gitea/gitea/tests/integration/integration_test.go:354
        	            				/home/runner/work/gitea/gitea/tests/integration/api_actions_run_test.go:278
        	Error:      	Not equal: 
        	            	expected: 200
        	            	actual  : 404
        	Test:       	TestAPIActionsGetWorkflowJobLogs
        	Messages:   	Request: GET /api/v1/repos/user2/repo2/actions/runs/795/jobs/192/logs

EDIT fixed the malformed pasted markdown due to bugs in GitHub Comments Markdown pasting....

Do you need to help how to run these tests locally?

Copy link
Copy Markdown
Contributor

@ChristopherHX ChristopherHX left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to do some required refactoring on your branch.

  • Tests look good
  • Test failures not so good
  • Copied code from webui needs to be refactored to be only defined once

Comment thread routers/common/actions.go Outdated
Comment thread routers/api/v1/repo/actions_run.go Outdated
Comment thread routers/api/v1/repo/actions_run.go Outdated
Comment thread routers/api/v1/repo/actions_run.go Outdated
Comment thread routers/api/v1/repo/actions_run.go Outdated
Comment thread routers/api/v1/repo/actions_run.go Outdated
Comment thread routers/api/v1/repo/actions_run.go Outdated
Comment thread routers/api/v1/repo/actions_run.go Outdated
@rossigee rossigee force-pushed the feature/runner-logs-api-endpoint branch from cf54846 to d3a10b3 Compare October 23, 2025 14:31
@rossigee
Copy link
Copy Markdown
Contributor Author

You might be using a wrong user account for tests, do they pass locally?

--- FAIL: TestAPIActionsRerunWorkflowRun (0.44s)

...

Tests seem to pass locally if I just run 'make test'.

Do you need to help how to run these tests locally?

I probably do 👍

@rossigee rossigee force-pushed the feature/runner-logs-api-endpoint branch 2 times, most recently from c7dc5ba to 4fae287 Compare April 26, 2026 08:30
@rossigee rossigee force-pushed the feature/runner-logs-api-endpoint branch 3 times, most recently from 0644c42 to 71bb9e7 Compare April 28, 2026 02:25
Comment thread tests/integration/api_actions_run_test.go
@bircni
Copy link
Copy Markdown
Member

bircni commented May 1, 2026

conflicts

@rossigee rossigee force-pushed the feature/runner-logs-api-endpoint branch 2 times, most recently from 3ff9e2d to 0200bf6 Compare May 3, 2026 02:59
@bircni
Copy link
Copy Markdown
Member

bircni commented May 3, 2026

whats the current state?

Comment thread services/actions/cancel.go
@lunny lunny added the type/enhancement An improvement of existing functionality label May 4, 2026
Comment thread services/convert/convert.go Outdated
Comment thread services/convert/convert.go Outdated
Comment thread services/convert/convert.go Outdated
@lunny lunny added this to the 1.27.0 milestone May 4, 2026
rossigee and others added 9 commits May 4, 2026 17:07
- Cancel and approve workflow runs via POST /runs/{run}/cancel|approve
- Download all job logs as zip via GET /runs/{run}/logs
- Download individual job log via GET /runs/{run}/jobs/{job_id}/logs
- Stream live log cursors via POST /runs/{run}/logs
- Add CreatedAt field to ActionWorkflowRun API response
- Extract shared log streaming and cancel logic into services/actions
- Move streaming log types to modules/structs
- Add Swagger documentation for all new endpoints
- Add integration tests with subtests for all new endpoints

Co-Authored-By: Claude Sonnet 4.6 <claude-sonnet-4-6@anthropic.com>
- Cast org.Visibility.String() to api.UserVisibility in ToOrganization
- Cast t.AccessMode.ToString() to api.AccessLevelName in ToTeams
- Update webhook notifier to pass repo to ToActionWorkflowRun

Co-Authored-By: Claude Sonnet 4.6 <claude-sonnet-4-6@anthropic.com>
Pass repo parameter to ToActionWorkflowRun in action.go and shared/action.go.

Co-Authored-By: Claude Sonnet 4.6 <claude-sonnet-4-6@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <claude-sonnet-4-6@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <claude-sonnet-4-6@anthropic.com>
- Add reqToken/reqRepoReader to GET log download endpoints for consistency
  with the POST streaming endpoint
- Remove spurious LoadRepos call in DownloadActionsRunAllJobLogs; jobs are
  already scoped to the repo by the query and Repo is never read
- Refactor reader.Close() in zip loop to use a closure with defer
- Update copyright year to 2026 on new services/actions/{cancel,log}.go
- Add TestAPIActionsListUserWorkflows and TestAPIActionsListRepoWorkflows
  as standalone top-level tests (were dropped when breaking up the
  orchestrator)
- Add idempotency assertion to TestAPIActionsApproveWorkflowRun: approving
  an already-approved run returns 400

Co-Authored-By: Claude Sonnet <claude-sonnet-4-6@anthropic.com>
GetWorkflowRunLogsStream (POST /runs/{run}/logs) and its supporting
code are moved to feature/runner-logs-stream-api for a standalone PR
where the POST-vs-GET design question can be resolved independently.

Removed from this branch:
- GetWorkflowRunLogsStream handler and route
- getRunJobsAndCurrent helper (only used by the stream handler)
- services/actions/log.go (ReadStepLogs)
- ActionLogCursor/Request/StepLine/Step/Response structs
- TestAPIActionsGetWorkflowRunLogsStream integration test
- Regenerated swagger specs accordingly

Co-Authored-By: Claude Sonnet <claude-sonnet-4-6@anthropic.com>
Replace the duplicated inline cancel logic in the web Cancel handler
with a call to actions_service.CancelRun, which encapsulates the same
transaction, commit status update, and notification logic.

Co-Authored-By: Claude Sonnet (claude-sonnet-4-6) <claude-sonnet-4-6@anthropic.com>
- Remove spurious context.ReferencesGitRepo(true) from /actions route group
- ToActionTask: restore targeted loads (LoadJob/LoadRun/LoadRepo) instead
  of LoadAttributes which unnecessarily loads task steps; use
  httplib.MakeAbsoluteURL(ctx, ...) for the URL field
- ToActionWorkflowRun: replace run.LoadAttributes with direct repo
  assignment + LoadTriggerUser to avoid redundant language-stats query;
  restore attempt.Run = run before attempt.LoadAttributes to prevent
  redundant DB re-fetch; restore ctx to APIURL/HTMLURL calls
- ToActionWorkflowJob: restore ctx to APIURL and HTMLURL calls

Co-Authored-By: Claude <claude-sonnet-4-6@anthropic.com>
@rossigee rossigee force-pushed the feature/runner-logs-api-endpoint branch from 1e1d0f5 to e38010e Compare May 4, 2026 10:08
triggerUser = attempt.TriggerUser
if attempt.Attempt > 1 {
previousAttemptURL = new(fmt.Sprintf("%s/actions/runs/%d/attempts/%d", run.Repo.APIURL(ctx), run.ID, attempt.Attempt-1))
url := fmt.Sprintf("%s/actions/runs/%d/attempts/%d", repo.APIURL(ctx), run.ID, attempt.Attempt-1)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the change is necessary?

@silverwind
Copy link
Copy Markdown
Member

silverwind commented May 4, 2026

Aggregated review (Claude Opus 4.7 + opencode/gpt-5.4)

Correctness — confirmed bugs

  1. DownloadActionsRunAllJobLogs skips rerun jobs. routers/common/actions.go:432-436 uses job.TaskID directly, both for the == 0 skip and the GetTaskByID(ctx, job.TaskID) lookup. The existing single-job path at routers/common/actions.go:34 uses curJob.EffectiveTaskID(), which falls back to SourceTaskID for rerun rows. Result: /actions/runs/{run}/logs silently omits rerun-job logs that /jobs/{job_id}/logs still serves. Use EffectiveTaskID() consistently.

  2. shared.ListRuns nil-deref risk. routers/api/v1/shared/action.go:200 passes runs[i].Repo to the new ToActionWorkflowRun. LoadRepos runs above it, but if a run's repo has been deleted, runs[i].Repo is nil and the convert function will nil-deref on repo.APIURL(ctx). Old code returned an error from LoadRepo. Add a nil guard or skip such rows.

  3. ApproveWorkflowRun returns stale run state. routers/api/v1/repo/actions_run.go:215-220 reloads the run immediately after ApproveRuns, but status/timestamps update asynchronously via the emitter/notifier. The response payload can still show Status: blocked even though approval succeeded. Either document this, or wait for status to settle before responding.

Design / API smells

  1. ToActionWorkflowRun mixed-source semantics. services/convert/convert.go:625-628 only assigns repo to run.Repo if nil, but always uses the parameter for URL construction. Inconsistent if caller passes a repo differing from a pre-set run.Repo. Pick one source.

  2. ApproveWorkflowRun non-idempotent. routers/api/v1/repo/actions_run.go:201 returns 400 when !run.NeedApproval. The test labels this case idempotency but it's the opposite. GitHub's equivalent is idempotent; consider returning 200 on the no-op path.

  3. Mid-stream zip errors corrupt response. routers/common/actions.go:401-469 writes headers + zip body directly to ctx.Resp. If OpenLogs/io.Copy fails mid-stream, the handler then calls ctx.APIErrorInternal on top of a partial 200 zip body. Drop to log.Error once headers are flushed.

  4. Misleading Zip Slip comment at routers/common/actions.go:445 — Zip Slip is an extraction-time bug, not a creation-time one. Sanitization is fine; rename the comment.

Conventions / churn

  1. Unrelated DecodeJSONjson.Unmarshal rewrite in tests/integration/api_actions_run_test.go. DecodeJSON uses a case-insensitive decoder deliberately (per its FIXME comment); strict json.Unmarshal is a silent behavior change for the existing tests with no stated reason. Bloats the diff — recommend reverting.

  2. Unrelated NewRequest(...).AddTokenAuth(...) line splits in the same file. Reformatting noise.

  3. Stray blank line at services/convert/convert.go:606 inside ToActionTask.

Test coverage gaps

  1. TestAPIActionsCancelWorkflowRun/Success only asserts 200 — no state assertion.

  2. TestAPIActionsGetWorkflowRunLogs/Success only asserts 200 — doesn't verify zip validity or filename headers.

  3. No test for ApproveWorkflowRun with a writer-but-non-admin token. The current ForbiddenWithoutPermission case uses a user with no permission at all.

  4. No test for the rerun-job log scenario in finding 1.

Permissions / security

No auth-bypass concerns. New mutating endpoints use reqRepoWriter(unit.TypeActions); getRunID correctly scopes by ctx.Repo.Repository.ID.

Verdict

Two real correctness bugs (rerun-job log omission, nil-deref in shared.ListRuns), one response-consistency issue (stale approve payload), plus design and churn cleanups. The rerun-job log bug is the highest-priority fix — it silently produces wrong output for the most common re-run a failed workflow then download logs flow.


Review by @silverwind, written with Claude Opus 4.7 and opencode/gpt-5.4.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lgtm/need 2 This PR needs two approvals by maintainers to be considered for merging. topic/gitea-actions related to the actions of Gitea type/enhancement An improvement of existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants