diff --git a/act/common/git/git.go b/act/common/git/git.go index 8f61eace..2b31367e 100644 --- a/act/common/git/git.go +++ b/act/common/git/git.go @@ -304,7 +304,7 @@ func shallowPinnedClone(ctx context.Context, input NewGitCloneExecutorInput) err } func fetch(ctx context.Context, input NewGitCloneExecutorInput) error { - args := []string{"fetch", "--force", "--prune"} + args := []string{"fetch", "--force", "--no-prune"} if _, err := os.Stat(filepath.Join(input.Dir, ".git", "shallow")); err == nil { args = append(args, "--depth", "1", "--no-tags") } diff --git a/act/common/git/git_test.go b/act/common/git/git_test.go index faf33aeb..5f74edf0 100644 --- a/act/common/git/git_test.go +++ b/act/common/git/git_test.go @@ -354,6 +354,8 @@ func TestGitCloneExecutorFollowsRemoteRefChanges(t *testing.T) { dir := t.TempDir() clone := NewGitCloneExecutor(NewGitCloneExecutorInput{URL: remoteDir, Ref: "main", Dir: dir}) require.NoError(t, clone(t.Context())) + require.NoError(t, gitCmd("-C", dir, "config", "fetch.prune", "true")) + require.NoError(t, clone(t.Context())) require.NoError(t, gitCmd("-C", workDir, "reset", "--hard", "HEAD~1")) require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "feature-rewritten")) @@ -361,11 +363,20 @@ func TestGitCloneExecutorFollowsRemoteRefChanges(t *testing.T) { require.NoError(t, clone(t.Context()), "a non-fast-forward ref must update") assert.Equal(t, "feature-rewritten", gitHeadSubject(t, dir)) - tagClone := NewGitCloneExecutor(NewGitCloneExecutorInput{URL: remoteDir, Ref: "v9", Dir: t.TempDir()}) + tagDir := t.TempDir() + tagClone := NewGitCloneExecutor(NewGitCloneExecutorInput{URL: remoteDir, Ref: "v9", Dir: tagDir}) require.Error(t, tagClone(t.Context())) require.NoError(t, gitCmd("-C", workDir, "tag", "v9")) require.NoError(t, gitCmd("-C", workDir, "push", "origin", "v9")) require.NoError(t, tagClone(t.Context()), "a tag published after a failed lookup must be fetched") + + require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "tag-moved")) + require.NoError(t, gitCmd("-C", workDir, "tag", "--force", "v9")) + require.NoError(t, gitCmd("-C", workDir, "push", "--force", "origin", "v9")) + for range 2 { + require.NoError(t, tagClone(t.Context()), "a moved tag must update and stay current") + assert.Equal(t, "tag-moved", gitHeadSubject(t, tagDir)) + } } func TestGitCloneExecutorOfflineMode(t *testing.T) { diff --git a/e2e/action_ref_test.go b/e2e/action_ref_test.go new file mode 100644 index 00000000..672865df --- /dev/null +++ b/e2e/action_ref_test.go @@ -0,0 +1,48 @@ +// Copyright 2026 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +//go:build e2e + +package e2e + +import ( + "fmt" + "os" + "testing" +) + +func testMovingActionRef(t *testing.T) { + t.Parallel() + ctx := t.Context() + + api, repo, _ := startIsolatedScenario(t, "action_ref.yml", "e2e-action-ref", runnerOptions{}) + const actionRepo = "e2e-moving-action" + if err := api.CreateRepo(ctx, actionRepo); err != nil { + t.Fatalf("create action repo: %v", err) + } + action, err := os.ReadFile("testdata/actions/moving.yml") + if err != nil { + t.Fatalf("read action fixture: %v", err) + } + if err := api.CreateFile(ctx, actionRepo, "action.yml", string(action), "add action"); err != nil { + t.Fatalf("push action: %v", err) + } + branch, err := api.DefaultBranch(ctx, repo) + if err != nil { + t.Fatalf("get default branch: %v", err) + } + + var previousID int64 + for revision := range 3 { + name := fmt.Sprintf("revision-%d", revision) + if err := api.CreateFile(ctx, actionRepo, name, name, "add "+name); err != nil { + t.Fatalf("move the action ref: %v", err) + } + if err := waitForDispatch(ctx, api, repo, "action_ref.yml", branch, map[string]string{"revision": name}); err != nil { + t.Fatalf("dispatch workflow: %v", err) + } + wfRun := waitForRunAfter(t, api, repo, previousID) + requireSuccess(t, api, repo, wfRun.ID) + previousID = wfRun.ID + } +} diff --git a/e2e/compatibility_test.go b/e2e/compatibility_test.go index b63ee06d..e160f430 100644 --- a/e2e/compatibility_test.go +++ b/e2e/compatibility_test.go @@ -31,6 +31,7 @@ func TestCompatibility(t *testing.T) { t.Run("cancellation_and_log_streaming", testRunCancellation) t.Run("dispatch", testWorkflowDispatch) t.Run("builtin_checkout", testBuiltinCheckout) + t.Run("moving_action_ref", testMovingActionRef) t.Run("ephemeral", testEphemeralRunner) t.Run("kubernetes", testKubernetes) } diff --git a/e2e/dispatch_test.go b/e2e/dispatch_test.go index 700a1819..97b6aa4e 100644 --- a/e2e/dispatch_test.go +++ b/e2e/dispatch_test.go @@ -23,7 +23,7 @@ func testWorkflowDispatch(t *testing.T) { t.Fatalf("get default branch: %v", err) } inputs := map[string]string{"subject": "dispatch-input-value"} - if err := waitForDispatch(ctx, api, repo, branch, inputs); err != nil { + if err := waitForDispatch(ctx, api, repo, "dispatch.yml", branch, inputs); err != nil { t.Fatalf("dispatch workflow: %v", err) } @@ -35,13 +35,13 @@ func testWorkflowDispatch(t *testing.T) { } // Gitea indexes workflows asynchronously after the Contents API commit. -func waitForDispatch(ctx context.Context, api *GiteaAPI, repo, branch string, inputs map[string]string) error { +func waitForDispatch(ctx context.Context, api *GiteaAPI, repo, workflow, branch string, inputs map[string]string) error { ctx, cancel := context.WithTimeout(ctx, time.Minute) defer cancel() var lastErr error for { - lastErr = api.DispatchWorkflow(ctx, repo, "dispatch.yml", branch, inputs) + lastErr = api.DispatchWorkflow(ctx, repo, workflow, branch, inputs) if lastErr == nil { return nil } diff --git a/e2e/testdata/actions/moving.yml b/e2e/testdata/actions/moving.yml new file mode 100644 index 00000000..baed2be2 --- /dev/null +++ b/e2e/testdata/actions/moving.yml @@ -0,0 +1,9 @@ +name: moving +inputs: + revision: + required: true +runs: + using: composite + steps: + - run: test -e "$GITHUB_ACTION_PATH/${{ inputs.revision }}" + shell: sh diff --git a/e2e/testdata/workflows/action_ref.yml b/e2e/testdata/workflows/action_ref.yml new file mode 100644 index 00000000..d5247869 --- /dev/null +++ b/e2e/testdata/workflows/action_ref.yml @@ -0,0 +1,13 @@ +name: action-ref +on: + workflow_dispatch: + inputs: + revision: + required: true +jobs: + moving: + runs-on: e2e-action-ref + steps: + - uses: self:e2e-admin/e2e-moving-action@main + with: + revision: ${{ inputs.revision }}