mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-04-29 19:28:59 -04:00
Some checks are pending
/ release (push) Waiting to run
testing-integration / test-unit (push) Waiting to run
testing-integration / test-sqlite (push) Waiting to run
testing-integration / test-mariadb (v10.6) (push) Waiting to run
testing-integration / test-mariadb (v11.8) (push) Waiting to run
testing / backend-checks (push) Waiting to run
testing / frontend-checks (push) Waiting to run
testing / test-unit (push) Blocked by required conditions
testing / test-e2e (push) Blocked by required conditions
testing / test-remote-cacher (redis) (push) Blocked by required conditions
testing / test-remote-cacher (valkey) (push) Blocked by required conditions
testing / test-remote-cacher (garnet) (push) Blocked by required conditions
testing / test-remote-cacher (redict) (push) Blocked by required conditions
testing / test-mysql (push) Blocked by required conditions
testing / test-pgsql (push) Blocked by required conditions
testing / test-sqlite (push) Blocked by required conditions
testing / security-check (push) Blocked by required conditions
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>
145 lines
5.1 KiB
Go
145 lines
5.1 KiB
Go
// 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
|
|
}
|