mirror of
https://gitea.com/gitea/act_runner
synced 2026-09-21 19:37:07 +02:00
perf: speed up action downloads (#1209)
An action pinned to a full commit hash cloned the whole repository, and a cached one hit the network on every run. Now only the pinned commit is fetched at depth 1, a cached commit is reused with no network at all, and the action's `.git` directory no longer ships into the job container, matching GitHub. ``` repo cold ms warm ms cache KiB actions/checkout 5807 → 787 1064 → 12 11492 → 2349 actions/setup-node 15257 → 1013 1365 → 21 64303 → 9924 actions/cache 18782 → 760 1589 → 16 60890 → 12507 actions/setup-go 5513 → 764 589 → 20 16600 → 9104 docker/login-action 25809 → 1225 1288 → 10 81709 → 12503 ``` Reviewed-on: https://gitea.com/gitea/runner/pulls/1209 Reviewed-by: bircni <bircni@icloud.com> Co-authored-by: silverwind <me@silverwind.io>
This commit is contained in:
+110
-6
@@ -16,6 +16,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -24,6 +25,9 @@ import (
|
||||
|
||||
gogit "github.com/go-git/go-git/v5"
|
||||
gogitconfig "github.com/go-git/go-git/v5/config"
|
||||
"github.com/go-git/go-git/v5/plumbing/transport"
|
||||
gogitclient "github.com/go-git/go-git/v5/plumbing/transport/client"
|
||||
gogitfile "github.com/go-git/go-git/v5/plumbing/transport/file"
|
||||
log "github.com/sirupsen/logrus"
|
||||
logrustest "github.com/sirupsen/logrus/hooks/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -435,7 +439,7 @@ func TestGitCloneExecutorShallow(t *testing.T) {
|
||||
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", m))
|
||||
}
|
||||
require.NoError(t, gitCmd("-C", workDir, "tag", "v1"))
|
||||
sha := gitRevParse(t, workDir, "HEAD~1") // c2, a SHA that go-git cannot shallow-clone
|
||||
unadvertisedSHA := gitRevParse(t, workDir, "HEAD~1")
|
||||
require.NoError(t, gitCmd("-C", workDir, "push", "-u", "origin", "main"))
|
||||
require.NoError(t, gitCmd("-C", workDir, "push", "origin", "v1"))
|
||||
|
||||
@@ -461,14 +465,43 @@ func TestGitCloneExecutorShallow(t *testing.T) {
|
||||
assert.Equal(t, "c3", gitHeadSubject(t, dir))
|
||||
})
|
||||
|
||||
t.Run("SHA falls back to a full clone", func(t *testing.T) {
|
||||
unadvertisedRemote := func(t *testing.T, allowUnadvertised bool) string {
|
||||
remote := filepath.Join(t.TempDir(), "remote.git")
|
||||
require.NoError(t, gitCmd("clone", "--bare", remoteDir, remote))
|
||||
require.NoError(t, gitCmd("-C", remote, "config", "uploadpack.allowAnySHA1InWant", strconv.FormatBool(allowUnadvertised)))
|
||||
return remote
|
||||
}
|
||||
|
||||
t.Run("commit hash falls back to a full clone when the remote refuses unadvertised objects", func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
require.NoError(t, NewGitCloneExecutor(NewGitCloneExecutorInput{
|
||||
URL: remoteDir, Ref: sha, Dir: dir, Depth: 1,
|
||||
URL: unadvertisedRemote(t, false), Ref: unadvertisedSHA, Dir: dir, Depth: 1,
|
||||
})(t.Context()))
|
||||
// go-git cannot shallow-clone a raw SHA, so it falls back to a full clone; the absence of a shallow marker proves the fallback happened.
|
||||
assert.NoFileExists(t, shallowMarker(dir), "a SHA ref must not produce a shallow clone")
|
||||
assert.Equal(t, sha, gitRevParse(t, dir, "HEAD"))
|
||||
assert.NoFileExists(t, shallowMarker(dir))
|
||||
assert.Equal(t, unadvertisedSHA, gitRevParse(t, dir, "HEAD"))
|
||||
})
|
||||
|
||||
t.Run("commit hash is fetched shallowly, moved to another hash, then reused without reaching the remote", func(t *testing.T) {
|
||||
remote := unadvertisedRemote(t, true)
|
||||
dir := t.TempDir()
|
||||
cloneAt := func(ref string) error {
|
||||
return NewGitCloneExecutor(NewGitCloneExecutorInput{
|
||||
URL: remote, Ref: ref, Dir: dir, Depth: 1,
|
||||
})(t.Context())
|
||||
}
|
||||
|
||||
require.NoError(t, cloneAt(unadvertisedSHA))
|
||||
assert.FileExists(t, shallowMarker(dir))
|
||||
assert.Equal(t, 1, gitRevCount(t, dir))
|
||||
assert.Equal(t, unadvertisedSHA, gitRevParse(t, dir, "HEAD"))
|
||||
|
||||
olderSHA := gitRevParse(t, workDir, "HEAD~2")
|
||||
require.NoError(t, cloneAt(olderSHA))
|
||||
assert.Equal(t, olderSHA, gitRevParse(t, dir, "HEAD"))
|
||||
|
||||
require.NoError(t, os.RemoveAll(remote))
|
||||
require.NoError(t, cloneAt(olderSHA))
|
||||
assert.Equal(t, olderSHA, gitRevParse(t, dir, "HEAD"))
|
||||
})
|
||||
|
||||
t.Run("moving branch updates while staying shallow", func(t *testing.T) {
|
||||
@@ -491,6 +524,77 @@ func TestGitCloneExecutorShallow(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestGitCloneExecutorColdCloneSkipsRefresh(t *testing.T) {
|
||||
remoteDir := t.TempDir()
|
||||
require.NoError(t, gitCmd("init", "--bare", "--initial-branch=main", remoteDir))
|
||||
workDir := t.TempDir()
|
||||
require.NoError(t, gitCmd("clone", remoteDir, workDir))
|
||||
require.NoError(t, gitCmd("-C", workDir, "checkout", "-b", "main"))
|
||||
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "c1"))
|
||||
require.NoError(t, gitCmd("-C", workDir, "tag", "v1"))
|
||||
require.NoError(t, gitCmd("-C", workDir, "push", "-u", "origin", "main"))
|
||||
require.NoError(t, gitCmd("-C", workDir, "push", "origin", "v1"))
|
||||
|
||||
for name, tt := range map[string]struct {
|
||||
Ref string
|
||||
Depth int
|
||||
}{
|
||||
"shallow branch": {"main", 1},
|
||||
"full clone tag": {"v1", 0},
|
||||
} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
counter := installCountingTransport(t)
|
||||
require.NoError(t, NewGitCloneExecutor(NewGitCloneExecutorInput{
|
||||
URL: remoteDir, Ref: tt.Ref, Dir: t.TempDir(), Depth: tt.Depth,
|
||||
})(t.Context()))
|
||||
assert.Equal(t, int64(1), counter.sessions.Load())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGitCloneExecutorPinnedHashIgnoresShadowingRef(t *testing.T) {
|
||||
remoteDir := t.TempDir()
|
||||
require.NoError(t, gitCmd("init", "--bare", "--initial-branch=main", remoteDir))
|
||||
workDir := t.TempDir()
|
||||
require.NoError(t, gitCmd("clone", remoteDir, workDir))
|
||||
require.NoError(t, gitCmd("-C", workDir, "checkout", "-b", "main"))
|
||||
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "pinned"))
|
||||
pinned := gitRevParse(t, workDir, "HEAD")
|
||||
require.NoError(t, gitCmd("-C", workDir, "commit", "--allow-empty", "-m", "decoy"))
|
||||
require.NoError(t, gitCmd("-C", workDir, "tag", pinned))
|
||||
require.NoError(t, gitCmd("-C", workDir, "push", "-u", "origin", "main"))
|
||||
require.NoError(t, gitCmd("-C", workDir, "push", "origin", pinned))
|
||||
|
||||
for name, depth := range map[string]int{"shallow": 1, "full clone": 0} {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
require.NoError(t, NewGitCloneExecutor(NewGitCloneExecutorInput{
|
||||
URL: remoteDir, Ref: pinned, Dir: dir, Depth: depth,
|
||||
})(t.Context()))
|
||||
assert.Equal(t, pinned, gitRevParse(t, dir, "HEAD"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type countingTransport struct {
|
||||
transport.Transport
|
||||
sessions atomic.Int64
|
||||
}
|
||||
|
||||
func (c *countingTransport) NewUploadPackSession(ep *transport.Endpoint, auth transport.AuthMethod) (transport.UploadPackSession, error) {
|
||||
c.sessions.Add(1)
|
||||
return c.Transport.NewUploadPackSession(ep, auth)
|
||||
}
|
||||
|
||||
func installCountingTransport(t *testing.T) *countingTransport {
|
||||
t.Helper()
|
||||
counter := &countingTransport{Transport: gogitfile.DefaultClient}
|
||||
gogitclient.InstallProtocol("file", counter)
|
||||
t.Cleanup(func() { gogitclient.InstallProtocol("file", gogitfile.DefaultClient) })
|
||||
|
||||
return counter
|
||||
}
|
||||
|
||||
func gitRevParse(t *testing.T, dir, rev string) string {
|
||||
t.Helper()
|
||||
out, err := exec.Command("git", "-C", dir, "rev-parse", rev).Output()
|
||||
|
||||
Reference in New Issue
Block a user