From a1f5dd767729e30d07ab42fda80c19f30a72679f Mon Sep 17 00:00:00 2001 From: sillyguodong <33891828+sillyguodong@users.noreply.github.com> Date: Fri, 8 Mar 2024 14:14:35 +0800 Subject: [PATCH] Make runs-on support variable expression (#29468) As title. Close issue: https://gitea.com/gitea/act_runner/issues/445 Follow: https://gitea.com/gitea/act/pulls/91 Move `getSecretsOfTask` and `getVariablesOfTask` under `models` because of circular dependency issues. --- go.mod | 2 +- go.sum | 4 +- models/actions/variable.go | 33 ++++++++++++ models/secret/secret.go | 39 ++++++++++++++ routers/api/actions/runner/utils.go | 80 +++++------------------------ services/actions/notifier_helper.go | 13 ++++- 6 files changed, 99 insertions(+), 72 deletions(-) diff --git a/go.mod b/go.mod index dfd9a95ea3..9b70a191ce 100644 --- a/go.mod +++ b/go.mod @@ -302,7 +302,7 @@ replace github.com/hashicorp/go-version => github.com/6543/go-version v1.3.1 replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142115-2c99e1ffdfa0 -replace github.com/nektos/act => gitea.com/gitea/act v0.2.51 +replace github.com/nektos/act => gitea.com/gitea/act v0.259.1 replace github.com/gorilla/feeds => github.com/yardenshoham/feeds v0.0.0-20240110072658-f3d0c21c0bd5 diff --git a/go.sum b/go.sum index 0d8e7dc699..a44809dde5 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078 h1:cliQ4HHsCo6xi2oWZYKWW4bly/Ory9FuTpFPRxj/mAg= git.sr.ht/~mariusor/go-xsd-duration v0.0.0-20220703122237-02e73435a078/go.mod h1:g/V2Hjas6Z1UHUp4yIx6bATpNzJ7DYtD0FG3+xARWxs= -gitea.com/gitea/act v0.2.51 h1:gXc/B4OlTciTTzAx9cmNyw04n2SDO7exPjAsR5Idu+c= -gitea.com/gitea/act v0.2.51/go.mod h1:CoaX2053jqBlD6JMgu4d4UgFL/rp2I14Kt5mMqcs0Z0= +gitea.com/gitea/act v0.259.1 h1:8GG1o/xtUHl3qjn5f0h/2FXrT5ubBn05TJOM5ry+FBw= +gitea.com/gitea/act v0.259.1/go.mod h1:UxZWRYqQG2Yj4+4OqfGWW5a3HELwejyWFQyU7F1jUD8= gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669 h1:RUBX+MK/TsDxpHmymaOaydfigEbbzqUnG1OTZU/HAeo= gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669/go.mod h1:77TZu701zMXWJFvB8gvTbQ92zQ3DQq/H7l5wAEjQRKc= gitea.com/go-chi/cache v0.0.0-20210110083709-82c4c9ce2d5e/go.mod h1:k2V/gPDEtXGjjMGuBJiapffAXTv76H4snSmlJRLUhH0= diff --git a/models/actions/variable.go b/models/actions/variable.go index 12717e0ae4..14ded60fac 100644 --- a/models/actions/variable.go +++ b/models/actions/variable.go @@ -10,6 +10,7 @@ import ( "strings" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -82,3 +83,35 @@ func UpdateVariable(ctx context.Context, variable *ActionVariable) (bool, error) }) return count != 0, err } + +func GetVariablesOfRun(ctx context.Context, run *ActionRun) (map[string]string, error) { + variables := map[string]string{} + + // Global + globalVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{}) + if err != nil { + log.Error("find global variables: %v", err) + return nil, err + } + + // Org / User level + ownerVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{OwnerID: run.Repo.OwnerID}) + if err != nil { + log.Error("find variables of org: %d, error: %v", run.Repo.OwnerID, err) + return nil, err + } + + // Repo level + repoVariables, err := db.Find[ActionVariable](ctx, FindVariablesOpts{RepoID: run.RepoID}) + if err != nil { + log.Error("find variables of repo: %d, error: %v", run.RepoID, err) + return nil, err + } + + // Level precedence: Repo > Org / User > Global + for _, v := range append(globalVariables, append(ownerVariables, repoVariables...)...) { + variables[v.Name] = v.Data + } + + return variables, nil +} diff --git a/models/secret/secret.go b/models/secret/secret.go index 41e860d7f6..35bed500b9 100644 --- a/models/secret/secret.go +++ b/models/secret/secret.go @@ -9,7 +9,10 @@ import ( "fmt" "strings" + actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" + actions_module "code.gitea.io/gitea/modules/actions" + "code.gitea.io/gitea/modules/log" secret_module "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" @@ -112,3 +115,39 @@ func UpdateSecret(ctx context.Context, secretID int64, data string) error { } return err } + +func GetSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) (map[string]string, error) { + secrets := map[string]string{} + + secrets["GITHUB_TOKEN"] = task.Token + secrets["GITEA_TOKEN"] = task.Token + + if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget { + // ignore secrets for fork pull request, except GITHUB_TOKEN and GITEA_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}) + if err != nil { + log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err) + return nil, err + } + repoSecrets, err := db.Find[Secret](ctx, FindSecretsOptions{RepoID: task.Job.Run.RepoID}) + if err != nil { + log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err) + return nil, err + } + + for _, secret := range append(ownerSecrets, repoSecrets...) { + v, err := secret_module.DecryptSecret(setting.SecretKey, secret.Data) + if err != nil { + log.Error("decrypt secret %v %q: %v", secret.ID, secret.Name, err) + return nil, err + } + secrets[secret.Name] = v + } + + return secrets, nil +} diff --git a/routers/api/actions/runner/utils.go b/routers/api/actions/runner/utils.go index a7cb31288c..ff6ec5bd54 100644 --- a/routers/api/actions/runner/utils.go +++ b/routers/api/actions/runner/utils.go @@ -15,7 +15,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" - secret_module "code.gitea.io/gitea/modules/secret" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/actions" @@ -32,14 +31,24 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv return nil, false, nil } + secrets, err := secret_model.GetSecretsOfTask(ctx, t) + if err != nil { + return nil, false, fmt.Errorf("GetSecretsOfTask: %w", err) + } + + vars, err := actions_model.GetVariablesOfRun(ctx, t.Job.Run) + if err != nil { + return nil, false, fmt.Errorf("GetVariablesOfRun: %w", err) + } + actions.CreateCommitStatus(ctx, t.Job) task := &runnerv1.Task{ Id: t.ID, WorkflowPayload: t.Job.WorkflowPayload, Context: generateTaskContext(t), - Secrets: getSecretsOfTask(ctx, t), - Vars: getVariablesOfTask(ctx, t), + Secrets: secrets, + Vars: vars, } if needs, err := findTaskNeeds(ctx, t); err != nil { @@ -55,71 +64,6 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv return task, true, nil } -func getSecretsOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string { - secrets := map[string]string{} - - secrets["GITHUB_TOKEN"] = task.Token - secrets["GITEA_TOKEN"] = task.Token - - if task.Job.Run.IsForkPullRequest && task.Job.Run.TriggerEvent != actions_module.GithubEventPullRequestTarget { - // ignore secrets for fork pull request, except GITHUB_TOKEN and GITEA_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 - } - - ownerSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{OwnerID: task.Job.Run.Repo.OwnerID}) - if err != nil { - log.Error("find secrets of owner %v: %v", task.Job.Run.Repo.OwnerID, err) - // go on - } - repoSecrets, err := db.Find[secret_model.Secret](ctx, secret_model.FindSecretsOptions{RepoID: task.Job.Run.RepoID}) - if err != nil { - log.Error("find secrets of repo %v: %v", task.Job.Run.RepoID, err) - // go on - } - - for _, secret := range append(ownerSecrets, repoSecrets...) { - if v, err := secret_module.DecryptSecret(setting.SecretKey, secret.Data); err != nil { - log.Error("decrypt secret %v %q: %v", secret.ID, secret.Name, err) - // go on - } else { - secrets[secret.Name] = v - } - } - - return secrets -} - -func getVariablesOfTask(ctx context.Context, task *actions_model.ActionTask) map[string]string { - variables := map[string]string{} - - // Global - globalVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{}) - if err != nil { - log.Error("find global variables: %v", err) - } - - // Org / User level - ownerVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{OwnerID: task.Job.Run.Repo.OwnerID}) - if err != nil { - log.Error("find variables of org: %d, error: %v", task.Job.Run.Repo.OwnerID, err) - } - - // Repo level - repoVariables, err := db.Find[actions_model.ActionVariable](ctx, actions_model.FindVariablesOpts{RepoID: task.Job.Run.RepoID}) - if err != nil { - log.Error("find variables of repo: %d, error: %v", task.Job.Run.RepoID, err) - } - - // Level precedence: Repo > Org / User > Global - for _, v := range append(globalVariables, append(ownerVariables, repoVariables...)...) { - variables[v.Name] = v.Data - } - - return variables -} - func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct { event := map[string]any{} _ = json.Unmarshal([]byte(t.Job.Run.EventPayload), &event) diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index b0d848b5ad..d84191dca2 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -296,7 +296,18 @@ func handleWorkflows( run.NeedApproval = need } - jobs, err := jobparser.Parse(dwf.Content) + if err := run.LoadAttributes(ctx); err != nil { + log.Error("LoadAttributes: %v", err) + continue + } + + vars, err := actions_model.GetVariablesOfRun(ctx, run) + if err != nil { + log.Error("GetVariablesOfRun: %v", err) + continue + } + + jobs, err := jobparser.Parse(dwf.Content, jobparser.WithVars(vars)) if err != nil { log.Error("jobparser.Parse: %v", err) continue