mirror of
https://gitea.com/gitea/act_runner.git
synced 2026-10-31 20:37:43 +08:00
fix(exec): load local reusable workflows from disk with --no-skip-checkout (#1362)
`gitea-runner exec --no-skip-checkout` panics with a nil pointer dereference on any job that calls a local reusable workflow (`uses: ./.gitea/workflows/x.yml`). Fetching a local reusable workflow from the instance was gated on `NoSkipCheckout`, but it reads the repository and SHA from the task's preset context, which only the daemon has. `exec` always has the workflow on disk, so the fetch is now gated on the preset instead, and `--no-skip-checkout` only governs checkout again. Daemon behavior is unchanged. The `exec` e2e test now also runs the existing reusable caller/callee pair with `--no-skip-checkout`. Reviewed-on: https://gitea.com/gitea/runner/pulls/1362 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
@@ -23,13 +23,14 @@ func newLocalReusableWorkflowExecutor(rc *RunContext) common.Executor {
|
||||
return common.NewErrorExecutor(err)
|
||||
}
|
||||
|
||||
if !rc.Config.NoSkipCheckout {
|
||||
preset := rc.Config.PresetGitHubContext
|
||||
if preset == nil {
|
||||
// resolve the local workflow against the workspace root, not the process
|
||||
// working directory, so it is found regardless of where the runner is invoked
|
||||
return newReusableWorkflowExecutor(rc, rc.Config.Workdir, localWorkflow.Path, false)
|
||||
}
|
||||
|
||||
uses := fmt.Sprintf("%s/%s@%s", rc.Config.PresetGitHubContext.Repository, localWorkflow.Path, rc.Config.PresetGitHubContext.Sha)
|
||||
uses := fmt.Sprintf("%s/%s@%s", preset.Repository, localWorkflow.Path, preset.Sha)
|
||||
|
||||
remoteReusableWorkflow, err := newRemoteReusableWorkflow(rc.Config.GitHubInstance, uses)
|
||||
if err != nil {
|
||||
|
||||
@@ -122,7 +122,7 @@ func TestNewLocalReusableWorkflowExecutorFindsSameRepositoryPaths(t *testing.T)
|
||||
|
||||
for _, uses := range []string{"./.gitea/workflows/reusable.yml", "$/.gitea/workflows/reusable.yml"} {
|
||||
rc := &RunContext{
|
||||
Config: &Config{Workdir: workdir},
|
||||
Config: &Config{Workdir: workdir, NoSkipCheckout: true},
|
||||
Run: &model.Run{JobID: "job", Workflow: &model.Workflow{Jobs: map[string]*model.Job{"job": {Uses: uses}}}},
|
||||
}
|
||||
err := newLocalReusableWorkflowExecutor(rc)(t.Context())
|
||||
|
||||
@@ -266,7 +266,7 @@ func testCLIShutdownCancels(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func testExecArtifacts(t *testing.T) {
|
||||
func testExec(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx := t.Context()
|
||||
|
||||
@@ -274,18 +274,25 @@ func testExecArtifacts(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
workflow, err := os.ReadFile("testdata/workflows/exec_artifact.yml")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
workflows := t.TempDir()
|
||||
for _, name := range []string{"a.yml", "b.yml"} {
|
||||
if err := os.WriteFile(filepath.Join(workflows, name), workflow, 0o644); err != nil {
|
||||
workflows, workdir := t.TempDir(), t.TempDir()
|
||||
for dst, src := range map[string]string{
|
||||
filepath.Join(workflows, "a.yml"): "exec_artifact.yml",
|
||||
filepath.Join(workflows, "b.yml"): "exec_artifact.yml",
|
||||
filepath.Join(workflows, "reusable_caller.yml"): "reusable_caller.yml",
|
||||
filepath.Join(workdir, ".gitea", "workflows", "reusable_callee.yml"): "reusable_callee.yml",
|
||||
} {
|
||||
content, err := os.ReadFile(filepath.Join("testdata", "workflows", src))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(dst, content, 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
workdir := t.TempDir()
|
||||
for _, args := range [][]string{
|
||||
{"init", "-q"},
|
||||
{"remote", "add", "origin", "https://example.com/e2e/exec.git"},
|
||||
@@ -297,15 +304,19 @@ func testExecArtifacts(t *testing.T) {
|
||||
}
|
||||
|
||||
artifacts := t.TempDir()
|
||||
args := []string{"exec", "-C", workdir, "-W", workflows, "-i", os.Getenv("E2E_JOB_IMAGE"), "--artifact-server-path", artifacts}
|
||||
args := []string{"exec", "-C", workdir, "-W", workflows, "-i", os.Getenv("E2E_JOB_IMAGE"), "--artifact-server-path", artifacts, "--no-skip-checkout"}
|
||||
if fixture.network != "" {
|
||||
args = append(args, "--network", fixture.network)
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, bin, args...)
|
||||
cmd.Env = slices.DeleteFunc(os.Environ(), func(kv string) bool { return strings.HasPrefix(kv, "ACTIONS_") })
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("exec: %v\n%s", err, out)
|
||||
}
|
||||
if !strings.Contains(string(out), "e2e-reusable-ran") {
|
||||
t.Errorf("the called workflow did not run:\n%s", out)
|
||||
}
|
||||
if blobs, _ := filepath.Glob(filepath.Join(artifacts, "v4", "*", "*", "blob")); len(blobs) != 2 {
|
||||
t.Fatalf("want one v4 artifact blob per workflow run, got %v", blobs)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ func TestCompatibility(t *testing.T) {
|
||||
t.Run("cancellation_and_log_streaming", testRunCancellation)
|
||||
t.Run("dispatch", testWorkflowDispatch)
|
||||
t.Run("builtin_checkout", testBuiltinCheckout)
|
||||
t.Run("exec_artifacts", testExecArtifacts)
|
||||
t.Run("exec", testExec)
|
||||
t.Run("moving_action_ref", testMovingActionRef)
|
||||
t.Run("ephemeral", testEphemeralRunner)
|
||||
t.Run("failure_paths", testFailurePaths)
|
||||
|
||||
Reference in New Issue
Block a user