mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2026-05-12 22:10:25 +00:00
feat: support workflow inputs on expanded reusable workflows (#10614)
Follow-up to #10525; adds support for `on.workflow_call.inputs` to expanded reusable workflows (when no `runs-on` is specified in a job that `uses: ...` another workflow).
The majority of the work for this is done by the `jobparser` library which evaluates inputs automatically when the job is being parsed and stores those inputs on the expanded jobs as the "default" value in `on.workflow_call.inputs`. Forgejo's role here is just to to ensure that `forgejo.event_name` is set to `"workflow_call"` when a job is dispatched, which causes the runner to use the inputs that are stored -- 34c20aa50f/act/runner/expression.go (L452-L467).
## 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**: https://code.forgejo.org/forgejo/end-to-end/pulls/1323
### 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.
- https://codeberg.org/forgejo/docs/pulls/1664
- [ ] 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.
<!--start release-notes-assistant-->
## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Features
- [PR](https://codeberg.org/forgejo/forgejo/pulls/10614): <!--number 10614 --><!--line 0 --><!--description c3VwcG9ydCB3b3JrZmxvdyBpbnB1dHMgb24gZXhwYW5kZWQgcmV1c2FibGUgd29ya2Zsb3dz-->support workflow inputs on expanded reusable workflows<!--description-->
<!--end release-notes-assistant-->
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/10614
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:
parent
60fb59a7a0
commit
f7d2f51bf7
5 changed files with 103 additions and 11 deletions
|
|
@ -287,3 +287,12 @@ func (job *ActionRunJob) IsWorkflowCallOuterJob() (bool, error) {
|
|||
}
|
||||
return jobWorkflow.Metadata.WorkflowCallID != "", nil
|
||||
}
|
||||
|
||||
// Check whether the target job was generated as a result of expanding a reusable workflow.
|
||||
func (job *ActionRunJob) IsWorkflowCallInnerJob() (bool, error) {
|
||||
jobWorkflow, err := job.DecodeWorkflowPayload()
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failure decoding workflow payload: %w", err)
|
||||
}
|
||||
return jobWorkflow.Metadata.WorkflowCallParent != "", nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -196,3 +196,40 @@ func TestActionRunJob_IsWorkflowCallOuterJob(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestActionRunJob_IsWorkflowCallInnerJob(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
job ActionRunJob
|
||||
isWorkflowCallInnerJob bool
|
||||
errContains string
|
||||
}{
|
||||
{
|
||||
name: "normal workflow",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("on: [workflow_dispatch]\nname: workflow")},
|
||||
isWorkflowCallInnerJob: false,
|
||||
},
|
||||
{
|
||||
name: "inner job",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("on:\n workflow_call:\nname: workflow\n__metadata:\n workflow_call_parent: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed\n")},
|
||||
isWorkflowCallInnerJob: true,
|
||||
},
|
||||
{
|
||||
name: "unparseable workflow",
|
||||
job: ActionRunJob{WorkflowPayload: []byte("name: []\nincomplete_runs_on: true")},
|
||||
errContains: "failure unmarshaling WorkflowPayload to SingleWorkflow: yaml: unmarshal errors",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
isWorkflowCallInnerJob, err := tt.job.IsWorkflowCallInnerJob()
|
||||
if tt.errContains != "" {
|
||||
assert.ErrorContains(t, err, tt.errContains)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.isWorkflowCallInnerJob, isWorkflowCallInnerJob)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,9 +80,20 @@ func generateGiteaContextForRun(run *actions_model.ActionRun) *model.GithubConte
|
|||
|
||||
// GenerateGiteaContext generate the gitea context without token and gitea_runtime_token
|
||||
// job can be nil when generating a context for parsing workflow-level expressions
|
||||
func GenerateGiteaContext(run *actions_model.ActionRun, job *actions_model.ActionRunJob) map[string]any {
|
||||
func GenerateGiteaContext(run *actions_model.ActionRun, job *actions_model.ActionRunJob) (map[string]any, error) {
|
||||
gitContextObj := generateGiteaContextForRun(run)
|
||||
|
||||
if job != nil {
|
||||
// Setting the `github.event_name` value to `workflow_call` while executing a reusable workflow's inner job
|
||||
// causes forgejo-runner to read `on.workflow_call.inputs` and populate its values into the `inputs` context.
|
||||
workflowCall, err := job.IsWorkflowCallInnerJob()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to inspect workflow call state: %w", err)
|
||||
} else if workflowCall {
|
||||
gitContextObj.EventName = "workflow_call"
|
||||
}
|
||||
}
|
||||
|
||||
gitContext, _ := githubContextToMap(gitContextObj)
|
||||
|
||||
// standard contexts, see https://docs.github.com/en/actions/learn-github-actions/contexts#github-context
|
||||
|
|
@ -108,7 +119,7 @@ func GenerateGiteaContext(run *actions_model.ActionRun, job *actions_model.Actio
|
|||
gitContext["run_attempt"] = fmt.Sprint(job.Attempt)
|
||||
}
|
||||
|
||||
return gitContext
|
||||
return gitContext, nil
|
||||
}
|
||||
|
||||
func githubContextToMap(gitContext *model.GithubContext) (map[string]any, error) {
|
||||
|
|
|
|||
|
|
@ -66,7 +66,8 @@ func TestGenerateGiteaContext(t *testing.T) {
|
|||
EventPayload: `{"repository": {"name": "testrepo"}}`,
|
||||
}
|
||||
|
||||
context := GenerateGiteaContext(run, nil)
|
||||
context, err := GenerateGiteaContext(run, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "testuser", context["actor"])
|
||||
assert.Equal(t, setting.AppURL+"api/v1", context["api_url"])
|
||||
|
|
@ -121,13 +122,15 @@ func TestGenerateGiteaContext(t *testing.T) {
|
|||
}
|
||||
|
||||
job := &actions_model.ActionRunJob{
|
||||
ID: 100,
|
||||
RunID: 1,
|
||||
JobID: "test-job",
|
||||
Attempt: 1,
|
||||
ID: 100,
|
||||
RunID: 1,
|
||||
JobID: "test-job",
|
||||
Attempt: 1,
|
||||
WorkflowPayload: []byte("on: [push]"),
|
||||
}
|
||||
|
||||
context := GenerateGiteaContext(run, job)
|
||||
context, err := GenerateGiteaContext(run, job)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "test-job", context["job"])
|
||||
assert.Equal(t, "1", context["run_id"])
|
||||
|
|
@ -166,7 +169,8 @@ func TestGenerateGiteaContext(t *testing.T) {
|
|||
EventPayload: string(payloadBytes),
|
||||
}
|
||||
|
||||
context := GenerateGiteaContext(run, nil)
|
||||
context, err := GenerateGiteaContext(run, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "main", context["base_ref"])
|
||||
assert.Equal(t, "feature-branch", context["head_ref"])
|
||||
|
|
@ -207,7 +211,8 @@ func TestGenerateGiteaContext(t *testing.T) {
|
|||
EventPayload: string(payloadBytes),
|
||||
}
|
||||
|
||||
context := GenerateGiteaContext(run, nil)
|
||||
context, err := GenerateGiteaContext(run, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "main", context["base_ref"])
|
||||
assert.Equal(t, "feature-branch", context["head_ref"])
|
||||
|
|
@ -218,6 +223,33 @@ func TestGenerateGiteaContext(t *testing.T) {
|
|||
assert.Equal(t, "branch", context["ref_type"])
|
||||
assert.Equal(t, "testowner/testrepo/.github/workflows/test-workflow.yml@refs/heads/main", context["workflow_ref"])
|
||||
})
|
||||
|
||||
t.Run("workflow_call job", func(t *testing.T) {
|
||||
run := &actions_model.ActionRun{
|
||||
ID: 1,
|
||||
Index: 42,
|
||||
TriggerUser: testUser,
|
||||
Repo: testRepo,
|
||||
TriggerEvent: "push",
|
||||
Ref: "refs/heads/main",
|
||||
CommitSHA: "abc123def456",
|
||||
WorkflowID: "test-workflow",
|
||||
EventPayload: `{}`,
|
||||
}
|
||||
|
||||
job := &actions_model.ActionRunJob{
|
||||
ID: 100,
|
||||
RunID: 1,
|
||||
JobID: "test-job",
|
||||
Attempt: 1,
|
||||
WorkflowPayload: []byte("on: { workflow_call: { inputs: {} } }\n__metadata:\n workflow_call_parent: b5a9f46f1f2513d7777fde50b169d323a6519e349cc175484c947ac315a209ed\n"),
|
||||
}
|
||||
|
||||
context, err := GenerateGiteaContext(run, job)
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, "workflow_call", context["event_name"])
|
||||
})
|
||||
}
|
||||
|
||||
func TestGenerateGiteaContextForRun(t *testing.T) {
|
||||
|
|
|
|||
|
|
@ -88,7 +88,10 @@ func generateTaskContext(t *actions_model.ActionTask) (*structpb.Struct, error)
|
|||
return nil, err
|
||||
}
|
||||
|
||||
gitCtx := GenerateGiteaContext(t.Job.Run, t.Job)
|
||||
gitCtx, err := GenerateGiteaContext(t.Job.Run, t.Job)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gitCtx["token"] = t.Token
|
||||
gitCtx["gitea_runtime_token"] = giteaRuntimeToken
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue