feat: support jobs.<job_id>.secrets with reusable workflow expansion (#10627)

Follow-up to #10525; adds support for `jobs.<job_id>.secrets` to expanded reusable workflows (when no `runs-on` is specified in a job that `uses: ...` another workflow).

## Checklist

The [contributor guide](https://forgejo.org/docs/next/contributor/) contains information that will be helpful to first time contributors. There also are a few [conditions for merging Pull Requests in Forgejo repositories](https://codeberg.org/forgejo/governance/src/branch/main/PullRequestsAgreement.md). You are also welcome to join the [Forgejo development chatroom](https://matrix.to/#/#forgejo-development:matrix.org).

### Tests

- I added test coverage for Go changes...
  - [x] in their respective `*_test.go` for unit tests.
  - [ ] in the `tests/integration` directory if it involves interactions with a live Forgejo server.
- I added test coverage for JavaScript changes...
  - [ ] in `web_src/js/*.test.js` if it can be unit tested.
  - [ ] in `tests/e2e/*.test.e2e.js` if it requires interactions with a live Forgejo server (see also the [developer guide for JavaScript testing](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/tests/e2e/README.md#end-to-end-tests)).
- **end-to-end testing**: [prepared, PR n](https://code.forgejo.org/forgejo/end-to-end/pulls/1351)

### Documentation

- [x] I created a pull request [to the documentation](https://codeberg.org/forgejo/docs) to explain to Forgejo users how to use this change.
    - [ ] Doc to be created
- [ ] I did not document these changes and I do not expect someone else to do it.

### Release notes

- [ ] I do not want this change to show in the release notes.
- [x] I want the title to show in the release notes with a link to this pull request.
- [ ] I want the content of the `release-notes/<pull request number>.md` to be be used for the release notes instead of the title.

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10627
Reviewed-by: Andreas Ahlenstorf <aahlenst@noreply.codeberg.org>
Co-authored-by: Mathieu Fenniak <mathieu@fenniak.net>
Co-committed-by: Mathieu Fenniak <mathieu@fenniak.net>
This commit is contained in:
Mathieu Fenniak 2025-12-30 17:33:21 +01:00 committed by Mathieu Fenniak
parent d8a5ee81fb
commit 9b2f7c557b
11 changed files with 654 additions and 33 deletions

View file

@ -208,6 +208,30 @@ func (run *ActionRun) SetDefaultConcurrencyGroup() {
))
}
func (run *ActionRun) FindOuterWorkflowCall(ctx context.Context, innerCall *ActionRunJob) (*ActionRunJob, error) {
allJobs, err := GetRunJobsByRunID(ctx, run.ID)
if err != nil {
return nil, fmt.Errorf("failure to get run jobs: %w", err)
}
if innerCall.workflowPayloadDecoded == nil || innerCall.workflowPayloadDecoded.Metadata.WorkflowCallParent == "" {
return nil, errors.New("invalid state for FindOuterWorkflowCall")
}
parent := innerCall.workflowPayloadDecoded.Metadata.WorkflowCallParent
for _, job := range allJobs {
if job.ID == innerCall.ID {
continue
}
swf, err := job.DecodeWorkflowPayload()
if err != nil {
return nil, err
}
if swf.Metadata.WorkflowCallID == parent {
return job, nil
}
}
return nil, fmt.Errorf("no workflow call with ID %s found in run %d", parent, run.ID)
}
func actionsCountOpenCacheKey(repoID int64) string {
return fmt.Sprintf("Actions:CountOpenActionRuns:%d", repoID)
}

View file

@ -272,3 +272,58 @@ jobs:
// Expect job with an incomplete runs-on to be StatusBlocked:
assert.Equal(t, StatusBlocked, job.Status)
}
func TestActionRun_FindOuterWorkflowCall(t *testing.T) {
require.NoError(t, unittest.PrepareTestDatabase())
pullRequestPosterID := int64(4)
repoID := int64(10)
pullRequestID := int64(2)
run := &ActionRun{
RepoID: repoID,
PullRequestID: pullRequestID,
PullRequestPosterID: pullRequestPosterID,
}
workflowRaw := []byte(`
jobs:
outer-job:
uses: ./.forgejo/workflows/reusable.yml
`)
workflows, err := jobparser.Parse(workflowRaw, false,
jobparser.WithJobOutputs(map[string]map[string]string{}),
jobparser.ExpandLocalReusableWorkflows(func(job *jobparser.Job, path string) ([]byte, error) {
return []byte(`
on:
workflow_call:
jobs:
inner-job-1:
runs-on: debian
steps: []
inner-job-2:
runs-on: debian
steps: []
`), nil
}))
require.NoError(t, err)
require.NoError(t, InsertRun(t.Context(), run, workflows))
jobs, err := db.Find[ActionRunJob](t.Context(), FindRunJobOptions{RunID: run.ID})
require.NoError(t, err)
require.Len(t, jobs, 3)
for _, j := range jobs {
t.Run(j.Name, func(t *testing.T) {
_, err := j.DecodeWorkflowPayload()
require.NoError(t, err)
outer, err := run.FindOuterWorkflowCall(t.Context(), j)
if j.Name == "outer-job" {
require.ErrorContains(t, err, "invalid state for FindOuterWorkflowCall")
} else {
require.NoError(t, err)
require.NotNil(t, outer)
assert.Equal(t, "outer-job", outer.Name)
}
})
}
}

View file

@ -8,9 +8,7 @@ import (
"fmt"
"strings"
actions_model "forgejo.org/models/actions"
"forgejo.org/models/db"
actions_module "forgejo.org/modules/actions"
"forgejo.org/modules/keying"
"forgejo.org/modules/log"
"forgejo.org/modules/timeutil"
@ -120,28 +118,17 @@ func (s *Secret) SetSecret(data string) {
s.Data = keying.ActionSecret.Encrypt([]byte(data), keying.ColumnAndID("data", s.ID))
}
func GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[string]string, error) {
func FetchActionSecrets(ctx context.Context, ownerID, repoID int64) (map[string]string, error) {
secrets := map[string]string{}
secrets["GITHUB_TOKEN"] = task.Token
secrets["GITEA_TOKEN"] = task.Token
secrets["FORGEJO_TOKEN"] = task.Token
if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget {
// ignore secrets for fork pull request, except GITHUB_TOKEN, GITEA_TOKEN and FORGEJO_TOKEN which are automatically generated.
// for the tasks triggered by pull_request_target event, they could access the secrets because they will run in the context of the base branch
// see the documentation: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
return secrets, nil
}
ownerSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID})
ownerSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{OwnerID: ownerID})
if err != nil {
log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err)
log.Error("find secrets of owner %v: %v", ownerID, err)
return nil, err
}
repoSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{RepoID: task.Job.Run.RepoID})
repoSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{RepoID: repoID})
if err != nil {
log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err)
log.Error("find secrets of repo %v: %v", repoID, err)
return nil, err
}

View file

@ -6,8 +6,6 @@ package secret
import (
"testing"
"forgejo.org/models/actions"
"forgejo.org/models/repo"
"forgejo.org/models/unittest"
"forgejo.org/modules/keying"
"forgejo.org/modules/util"
@ -85,17 +83,8 @@ func TestInsertEncryptedSecret(t *testing.T) {
})
})
t.Run("Get secrets", func(t *testing.T) {
secrets, err := GetSecretsOfTask(t.Context(), &actions.ActionTask{
Job: &actions.ActionRunJob{
Run: &actions.ActionRun{
RepoID: 1,
Repo: &repo.Repository{
OwnerID: 2,
},
},
},
})
t.Run("FetchActionSecrets", func(t *testing.T) {
secrets, err := FetchActionSecrets(t.Context(), 2, 1)
require.NoError(t, err)
assert.Equal(t, "some owner secret", secrets["OWNER_SECRET"])
assert.Equal(t, "some repository secret", secrets["REPO_SECRET"])

View file

@ -0,0 +1,94 @@
# Supporting data for Case 600
-
id: 900
title: "running"
owner_id: 2
repo_id: 63
workflow_id: "running.yaml"
index: 4
trigger_event: "push"
is_fork_pull_request: false
# Supporting data for Case 601
-
id: 901
title: "running"
owner_id: 2
repo_id: 63
workflow_id: "running.yaml"
index: 5
trigger_event: "pull_request_target"
is_fork_pull_request: false
# Supporting data for Case 602
-
id: 902
title: "running"
owner_id: 2
repo_id: 63
workflow_id: "running.yaml"
index: 6
trigger_event: "pull_request_target"
is_fork_pull_request: true
# Supporting data for Case 603
-
id: 903
title: "running"
owner_id: 2
repo_id: 63
workflow_id: "running.yaml"
index: 7
trigger_event: "pull_request"
is_fork_pull_request: false
# Supporting data for Case 604
-
id: 904
title: "running"
owner_id: 2
repo_id: 63
workflow_id: "running.yaml"
index: 8
trigger_event: "pull_request"
is_fork_pull_request: true
# Supporting data for Case 605
-
id: 905
title: "running"
owner_id: 2
repo_id: 63
workflow_id: "running.yaml"
index: 9
trigger_event: "pull_request"
is_fork_pull_request: false
# Supporting data for Case 607
-
id: 906
title: "running"
owner_id: 2
repo_id: 63
workflow_id: "running.yaml"
index: 10
trigger_event: "pull_request"
is_fork_pull_request: false
# Supporting data for Case 610
-
id: 907
title: "running"
owner_id: 2
repo_id: 63
ref: "refs/heads/main"
workflow_id: "running.yaml"
index: 11
trigger_event: "workflow_dispatch"
is_fork_pull_request: false
event_payload: |
{
"inputs": {
"some_wd_input": "some_wd_input_value"
}
}

View file

@ -0,0 +1,192 @@
# Case 600 -- on:push workflow with some secrets
-
id: 600
run_id: 900
workflow_payload: |
"on":
push:
jobs:
produce-artifacts:
name: produce-artifacts
runs-on: docker
steps:
- run: echo "OK!"
# Case 601 -- on: pull_request_target workflow, local PR (not fork)
-
id: 601
run_id: 901
workflow_payload: |
"on":
pull_request_target:
jobs:
produce-artifacts:
name: produce-artifacts
runs-on: docker
steps:
- run: echo "OK!"
# Case 602 -- on: pull_request_target workflow, fork PR
-
id: 602
run_id: 902
workflow_payload: |
"on":
pull_request_target:
jobs:
produce-artifacts:
name: produce-artifacts
runs-on: docker
steps:
- run: echo "OK!"
# Case 603 -- on: pull_request workflow, local PR (not fork)
-
id: 603
run_id: 903
workflow_payload: |
"on":
pull_request:
jobs:
produce-artifacts:
name: produce-artifacts
runs-on: docker
steps:
- run: echo "OK!"
# Case 604 -- on: pull_request workflow, fork PR
-
id: 604
run_id: 904
workflow_payload: |
"on":
pull_request:
jobs:
produce-artifacts:
name: produce-artifacts
runs-on: docker
steps:
- run: echo "OK!"
# Case 605 -- workflow call inner job, inherit secrets, 606 is the outer job
-
id: 605
run_id: 905
workflow_payload: |
"on":
pull_request_target:
jobs:
produce-artifacts:
name: produce-artifacts
runs-on: docker
steps:
- run: echo "OK!"
__metadata:
workflow_call_parent: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed
-
id: 606
run_id: 905
workflow_payload: |
"on":
pull_request_target:
jobs:
invoke-reusable:
uses: ./.forgejo/workflows/produce.yml
secrets: inherit
__metadata:
workflow_call_id: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed
# Case 607 -- workflow call two layer inner job, inherit secrets, 607->608->609
-
id: 607
run_id: 906
workflow_payload: |
"on":
workflow_call:
jobs:
produce-artifacts:
name: produce-artifacts
runs-on: docker
steps:
- run: echo "OK!"
__metadata:
workflow_call_parent: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed
-
id: 608
run_id: 906
workflow_payload: |
"on":
workflow_call:
jobs:
invoke-reusable:
uses: ./.forgejo/workflows/produce-specific.yml
secrets: inherit
__metadata:
workflow_call_id: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed
workflow_call_parent: 1976193ec4c48a92ba58816b34116272f5b3a612b91494956e5b53ee70b8714f
-
id: 609
run_id: 906
workflow_payload: |
"on":
pull_request:
jobs:
invoke-reusable:
uses: ./.forgejo/workflows/produce.yml
secrets:
secret_1: ${{ secrets.secret_1 }} -- but are you sure?
__metadata:
workflow_call_id: 1976193ec4c48a92ba58816b34116272f5b3a612b91494956e5b53ee70b8714f
# Case 610 -- workflow call specifically defined secrets, 611 is the outer job, 612 is another job in the same workflow
-
id: 610
run_id: 907
workflow_payload: |
"on":
workflow_call:
jobs:
produce-artifacts:
name: produce-artifacts
runs-on: docker
steps:
- run: echo "OK!"
__metadata:
workflow_call_parent: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed
-
id: 611
run_id: 907
needs: '["provide-outputs"]'
workflow_payload: |
"on":
workflow_dispatch:
jobs:
invoke-reusable:
uses: ./.forgejo/workflows/produce-specific.yml
secrets:
forgejo: context forgejo = ${{ forgejo.ref }}
inputs: context inputs = ${{ inputs.some_wd_input }}
matrix: context matrix = ${{ matrix.some-dimension }}
needs: context needs = ${{ needs.provide-outputs.outputs.some-output }}
secrets: context secrets = ${{ secrets.secret_1 }}
strategy: context strategy = ${{ strategy.fail-fast }}
vars: context vars = ${{ vars.repo_var }}
strategy:
fail-fast: false
matrix:
some-dimension:
- some-dimension-value
__metadata:
workflow_call_id: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed
-
id: 612
run_id: 907
job_id: provide-outputs
status: 1 # success
workflow_payload: |
"on":
workflow_dispatch:
jobs:
provide-outputs:
steps: []
task_id: 100

View file

@ -0,0 +1,6 @@
# Supporting data for Case 610
-
id: 100
task_id: 100
output_key: some-output
output_value: 'abcdefghijklmnopqrstuvwxyz'

View file

@ -0,0 +1,22 @@
# Case w/ action_run_job.id = 601
-
id: 1001
name: REPO_VAR
owner_id: 0
repo_id: 63
data: "this is a repo variable"
created_unix: 1737000000
-
id: 1002
name: ORG_VAR
owner_id: 2
repo_id: 0
data: "this is an org variable"
created_unix: 1737000000
-
id: 1003
name: GLOBAL_VAR
owner_id: 0
repo_id: 0
data: "this is a global variable"
created_unix: 1737000000

145
services/actions/secret.go Normal file
View file

@ -0,0 +1,145 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package actions
import (
"context"
"errors"
"fmt"
actions_model "forgejo.org/models/actions"
secret_model "forgejo.org/models/secret"
actions_module "forgejo.org/modules/actions"
"forgejo.org/modules/json"
"forgejo.org/modules/structs"
"code.forgejo.org/forgejo/runner/v12/act/jobparser"
)
func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[string]string, error) {
secrets, err := getSecretsOfJob(ctx, task.Job)
secrets["GITHUB_TOKEN"] = task.Token
secrets["GITEA_TOKEN"] = task.Token
secrets["FORGEJO_TOKEN"] = task.Token
return secrets, err
}
func getSecretsOfJob(ctx context.Context, job *actions_model.ActionRunJob) (map[string]string, error) {
isInnerWorkflowCall, err := job.IsWorkflowCallInnerJob()
if err != nil {
return nil, err
}
err = job.LoadRun(ctx)
if err != nil {
return nil, fmt.Errorf("failure to load job run: %w", err)
}
if isInnerWorkflowCall {
return getSecretsOfInnerWorkflowCall(ctx, job)
}
if job.Run.IsForkPullRequest && job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget {
// ignore secrets for fork pull request, except GITHUB_TOKEN, GITEA_TOKEN and FORGEJO_TOKEN which are automatically generated.
// for the tasks triggered by pull_request_target event, they could access the secrets because they will run in the context of the base branch
// see the documentation: https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
return map[string]string{}, nil
}
err = job.Run.LoadRepo(ctx)
if err != nil {
return nil, err
}
jobSecrets, err := secret_model.FetchActionSecrets(ctx, job.Run.Repo.OwnerID, job.Run.RepoID)
if err != nil {
// Don't return error details, just in case they contain confidential details and error reaches a user;
// FetchActionSecrets logs all errors to the server log.
return nil, errors.New("failure to fetch secrets")
}
return jobSecrets, nil
}
func getSecretsOfInnerWorkflowCall(ctx context.Context, job *actions_model.ActionRunJob) (map[string]string, error) {
// Workflow calls can have two different behaviours -- they can either have `secrets: inherit` in which case we get
// the secrets of the caller and pass them in, or, they can have `secrets: { ... }` with key-values that need to be
// evaluated in the context of the parent (that is, `${{ secret.example_secret }}` would reference `example_secret`
// from the caller's secrets).
//
// In either case, we need the caller job's secrets, and we need the caller job's workflow definition to find out
// how they wanted secrets defined for this workflow call.
outerWorkflowCall, err := job.Run.FindOuterWorkflowCall(ctx, job)
if err != nil {
return nil, fmt.Errorf("failure to find outer workflow call: %w", err)
}
outerSecrets, err := getSecretsOfJob(ctx, outerWorkflowCall)
if err != nil {
return nil, err
}
outerWorkflowPayload, err := outerWorkflowCall.DecodeWorkflowPayload()
if err != nil {
return nil, err
}
_, outerJob := outerWorkflowPayload.Job()
if outerJob.InheritSecrets() {
return outerSecrets, nil
}
// Gather all the data that is needed to perform an expression evaluation of the parent job's secrets context:
err = outerWorkflowCall.LoadRun(ctx)
if err != nil {
return nil, fmt.Errorf("failure to load job's run: %w", err)
}
err = outerWorkflowCall.Run.LoadRepo(ctx)
if err != nil {
return nil, fmt.Errorf("failure to load run's repo: %w", err)
}
githubContext := generateGiteaContextForRun(outerWorkflowCall.Run)
taskNeeds, err := FindTaskNeeds(ctx, outerWorkflowCall)
if err != nil {
return nil, fmt.Errorf("failure evaluating 'needs' for job: %w", err)
}
needs := make([]string, 0, len(taskNeeds))
jobResults := make(map[string]string, len(taskNeeds))
jobOutputs := make(map[string]map[string]string, len(taskNeeds))
for jobID, n := range taskNeeds {
needs = append(needs, jobID)
jobResults[jobID] = n.Result.String()
jobOutputs[jobID] = n.Outputs
}
vars, err := actions_model.GetVariablesOfRun(ctx, job.Run)
if err != nil {
return nil, fmt.Errorf("failure evaluating 'vars' for run: %w", err)
}
var inputs map[string]any
if outerWorkflowCall.Run.TriggerEvent == actions_module.GithubEventWorkflowDispatch {
// workflow_dispatch inputs are stored in the event payload
var dispatchPayload *structs.WorkflowDispatchPayload
err := json.Unmarshal([]byte(outerWorkflowCall.Run.EventPayload), &dispatchPayload)
if err != nil {
return nil, fmt.Errorf("failure reading workflow dispatch payload: %w", err)
}
// transition from map[string]string to map[string]any...
inputs = make(map[string]any, len(dispatchPayload.Inputs))
for k, v := range dispatchPayload.Inputs {
inputs[k] = v
}
}
jobSecrets := jobparser.EvaluateWorkflowCallSecrets(&jobparser.EvaluateWorkflowCallSecretsArgs{
CallerWorkflow: outerWorkflowPayload,
CallerSecrets: outerSecrets,
GitCtx: githubContext,
Vars: vars,
Needs: needs,
JobResults: jobResults,
JobOutputs: jobOutputs,
JobInputs: inputs,
})
return jobSecrets, nil
}

View file

@ -0,0 +1,108 @@
// Copyright 2025 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: GPL-3.0-or-later
package actions
import (
"testing"
actions_model "forgejo.org/models/actions"
secret_model "forgejo.org/models/secret"
"forgejo.org/models/unittest"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGetSecretsOfJob(t *testing.T) {
tests := []struct {
name string
runJobID int64
secrets map[string]string
}{
{
name: "push run",
runJobID: 600,
secrets: map[string]string{
"SECRET_1": "the sky is blue",
"SECRET_2": "the ocean is also blue",
},
},
{
name: "on: pull_request_target workflow, local PR (not fork)",
runJobID: 601,
secrets: map[string]string{
"SECRET_1": "the sky is blue",
"SECRET_2": "the ocean is also blue",
},
},
{
name: "on: pull_request_target workflow, fork PR",
runJobID: 602,
secrets: map[string]string{
"SECRET_1": "the sky is blue",
"SECRET_2": "the ocean is also blue",
},
},
{
name: "on: pull_request workflow, local PR (not fork)",
runJobID: 603,
secrets: map[string]string{
"SECRET_1": "the sky is blue",
"SECRET_2": "the ocean is also blue",
},
},
{
name: "on: pull_request workflow, fork PR",
runJobID: 604,
secrets: map[string]string{},
},
{
name: "workflow call inner job, inherit secrets",
runJobID: 605,
secrets: map[string]string{
"SECRET_1": "the sky is blue",
"SECRET_2": "the ocean is also blue",
},
},
{
name: "workflow call two layer inner job, inherit secrets",
runJobID: 607,
secrets: map[string]string{
// Even though we're 'inherit' in this case, we're inheriting from the parent call which is a subset
// (and modification) of the secrets -- so shouldn't see SECRET_2.
"SECRET_1": "the sky is blue -- but are you sure?",
},
},
{
name: "workflow call inner job, defined secrets",
runJobID: 610,
secrets: map[string]string{
"FORGEJO": "context forgejo = refs/heads/main",
"INPUTS": "context inputs = some_wd_input_value",
"MATRIX": "context matrix = some-dimension-value",
"NEEDS": "context needs = abcdefghijklmnopqrstuvwxyz",
"SECRETS": "context secrets = the sky is blue",
"STRATEGY": "context strategy = false",
"VARS": "context vars = this is a repo variable",
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer unittest.OverrideFixtures("services/actions/TestGetSecretsOfJob")()
require.NoError(t, unittest.PrepareTestDatabase())
// Due to encryption, more maintainable to do this rather than create secrets in fixture data
_, err := secret_model.InsertEncryptedSecret(t.Context(), 2, 0, "secret_1", "the sky is blue")
require.NoError(t, err)
_, err = secret_model.InsertEncryptedSecret(t.Context(), 0, 63, "secret_2", "the ocean is also blue")
require.NoError(t, err)
runJob := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: tt.runJobID})
actualSecrets, err := getSecretsOfJob(t.Context(), runJob)
require.NoError(t, err)
assert.Equal(t, tt.secrets, actualSecrets)
})
}
}

View file

@ -10,7 +10,6 @@ import (
actions_model "forgejo.org/models/actions"
"forgejo.org/models/db"
secret_model "forgejo.org/models/secret"
"forgejo.org/modules/timeutil"
"forgejo.org/modules/util"
@ -39,7 +38,7 @@ func PickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv
}
job = t.Job
secrets, err := secret_model.GetSecretsOfTask(ctx, t)
secrets, err := getSecretsOfTask(ctx, t)
if err != nil {
return fmt.Errorf("GetSecretsOfTask: %w", err)
}