mirror of
https://gitea.com/gitea/act_runner
synced 2026-11-03 10:57:22 +01:00
fix: stop pruning the cached action ref on refresh (#1312)
The action cache refresh fetched with `--prune` and the unqualified refspec `+<ref>:refs/runner/<hash>`. Git cannot map that ref back to a remote ref, so it deleted it on every other refresh. The first job after a branch or tag moved then failed with `cannot lock ref`, and a moved tag in a full clone could silently run its old commit. `--no-prune` fixes this and also overrides an inherited `fetch.prune`. Fixes: https://gitea.com/gitea/runner/issues/1311 Reviewed-on: https://gitea.com/gitea/runner/pulls/1312 Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com> Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
This commit is contained in:
1 parent
470969dc74
commit
42858c61df
7 files changed
+87
-5
No files matched your search
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
name: moving
|
||||
inputs:
|
||||
revision:
|
||||
required: true
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- run: test -e "$GITHUB_ACTION_PATH/${{ inputs.revision }}"
|
||||
shell: sh
|
||||
+13
@@ -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 }}
|
||||
Reference in new issue
Block a user