Allow referencing inputs in jobs.<job_id>.runs-on (#9950)

This PR is a follow-up to https://code.forgejo.org/forgejo/runner/pulls/1117. That PR has to be merged before this one can proceed.

The objective is to allow referencing the `inputs` context in `jobs.<job_id>.runs-on`. That enables users to enter a label in the Forgejo UI during `workflow_dispatch`.

Example:

```yaml
name: test
on:
  workflow_dispatch:
    inputs:
      image:
        required: true
        type: string

jobs:
  test:
    runs-on: ${{ inputs.image }}
    steps:
      - run: echo "Running on ${{ inputs.image }}"
```

Using `inputs` with reusable workflows does not work. I haven't changed `schedule_tasks.go` because the `schedule` trigger does not support `inputs`.

<!--start release-notes-assistant-->

## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Features
  - [PR](https://codeberg.org/forgejo/forgejo/pulls/9950): <!--number 9950 --><!--line 0 --><!--description QWxsb3cgcmVmZXJlbmNpbmcgaW5wdXRzIGluIGpvYnMuPGpvYl9pZD4ucnVucy1vbg==-->Allow referencing inputs in jobs.<job_id>.runs-on<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/9950
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
Co-authored-by: Andreas Ahlenstorf <andreas@ahlenstorf.ch>
Co-committed-by: Andreas Ahlenstorf <andreas@ahlenstorf.ch>
This commit is contained in:
Andreas Ahlenstorf 2025-11-05 20:41:05 +01:00 committed by Mathieu Fenniak
commit addc393e71
2 changed files with 59 additions and 1 deletions

View file

@ -155,7 +155,7 @@ func (entry *Workflow) Dispatch(ctx context.Context, inputGetter InputValueGette
}
}
jobs, err := jobParser(content, jobparser.WithVars(vars))
jobs, err := jobParser(content, jobparser.WithVars(vars), jobparser.WithInputs(inputsAny))
if err != nil {
return nil, nil, err
}

View file

@ -9,6 +9,7 @@ import (
"net/http"
"net/url"
"reflect"
"strconv"
"testing"
"time"
@ -478,6 +479,63 @@ jobs:
})
}
func TestActionsRunsOnInputsWorkflowDispatch(t *testing.T) {
if !setting.Database.Type.IsSQLite3() {
t.Skip()
}
onApplicationRun(t, func(t *testing.T, u *url.URL) {
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
session := loginUser(t, user2.Name)
token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
testRepository := createActionsTestRepo(t, token, "actions-runs-on-inputs-workflow-dispatch", false)
ubuntuRunner := newMockRunner()
ubuntuRunner.registerAsRepoRunner(t, user2.Name, testRepository.Name, "ubuntu-runner", []string{"ubuntu"})
windowsRunner := newMockRunner()
windowsRunner.registerAsRepoRunner(t, user2.Name, testRepository.Name, "windows-runner", []string{"windows"})
workflowPath := ".gitea/workflows/pull.yaml"
workflow := `name: Test runs-on with inputs
on:
workflow_dispatch:
inputs:
image:
required: true
type: string
jobs:
test:
runs-on: ${{ inputs.image }}
steps:
- run: echo "Running on ${{ inputs.image }}"
`
options := getWorkflowCreateFileOptions(user2, testRepository.DefaultBranch, fmt.Sprintf("create %s", workflowPath), workflow)
createWorkflowFile(t, token, user2.Name, testRepository.Name, workflowPath, options)
url := fmt.Sprintf("/%s/%s/actions/manual", user2.Name, testRepository.Name)
request := NewRequestWithValues(t, "POST", url, map[string]string{
"inputs[image]": "windows",
"ref": testRepository.DefaultBranch,
"workflow": "pull.yaml",
"actor": strconv.FormatInt(user2.ID, 10),
})
session.MakeRequest(t, request, http.StatusSeeOther)
assert.Nil(t, ubuntuRunner.maybeFetchTask(t))
task := windowsRunner.fetchTask(t)
actionTask := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionTask{ID: task.Id})
actionRunJob := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: actionTask.JobID})
run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{ID: actionRunJob.RunID})
assert.Equal(t, "Test runs-on with inputs", run.Title)
})
}
func createActionsTestRepo(t *testing.T, authToken, repoName string, isPrivate bool) *api.Repository {
req := NewRequestWithJSON(t, "POST", "/api/v1/user/repos", &api.CreateRepoOption{
Name: repoName,