Skip to content

Commit 5b4393f

Browse files
feat: make use new action cache the new default for downloading actions (#12)
* remove legacy action caching * migrate tests * clear old legacy action cache when run
1 parent 3d68444 commit 5b4393f

11 files changed

+151
-281
lines changed

cmd/input.go

-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ type Input struct {
5858
actionOfflineMode bool
5959
logPrefixJobID bool
6060
networkName string
61-
useNewActionCache bool
6261
localRepository []string
6362
}
6463

cmd/root.go

+26-22
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,6 @@ func Execute(ctx context.Context, version string) {
102102
rootCmd.PersistentFlags().StringVarP(&input.actionCachePath, "action-cache-path", "", filepath.Join(CacheHomeDir, "act"), "Defines the path where the actions get cached and host workspaces created.")
103103
rootCmd.PersistentFlags().BoolVarP(&input.actionOfflineMode, "action-offline-mode", "", false, "If action contents exists, it will not be fetch and pull again. If turn on this, will turn off force pull")
104104
rootCmd.PersistentFlags().StringVarP(&input.networkName, "network", "", "host", "Sets a docker network name. Defaults to host.")
105-
rootCmd.PersistentFlags().BoolVarP(&input.useNewActionCache, "use-new-action-cache", "", false, "Enable using the new Action Cache for storing Actions locally")
106105
rootCmd.PersistentFlags().StringArrayVarP(&input.localRepository, "local-repository", "", []string{}, "Replaces the specified repository and ref with a local folder (e.g. https://github.com/test/test@v0=/home/act/test or test/test@v0=/home/act/test, the latter matches any hosts or protocols)")
107106
rootCmd.SetArgs(args())
108107

@@ -393,6 +392,16 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
393392
vars := newSecrets(input.vars)
394393
_ = readEnvs(input.Varfile(), vars)
395394

395+
log.Debugf("Cleaning up %s old action cache format", input.actionCachePath)
396+
entries, _ := os.ReadDir(input.actionCachePath)
397+
for _, entry := range entries {
398+
if strings.Contains(entry.Name(), "@") {
399+
fullPath := filepath.Join(input.actionCachePath, entry.Name())
400+
log.Debugf("Removing %s", fullPath)
401+
_ = os.RemoveAll(fullPath)
402+
}
403+
}
404+
396405
matrixes := parseMatrix(input.matrix)
397406
log.Debugf("Evaluated matrix inclusions: %v", matrixes)
398407

@@ -588,31 +597,26 @@ func newRunCommand(ctx context.Context, input *Input) func(*cobra.Command, []str
588597
Matrix: matrixes,
589598
ContainerNetworkMode: docker_container.NetworkMode(input.networkName),
590599
}
591-
if input.useNewActionCache || len(input.localRepository) > 0 {
592-
if input.actionOfflineMode {
593-
config.ActionCache = &runner.GoGitActionCacheOfflineMode{
594-
Parent: runner.GoGitActionCache{
595-
Path: config.ActionCacheDir,
596-
},
597-
}
598-
} else {
599-
config.ActionCache = &runner.GoGitActionCache{
600+
if input.actionOfflineMode {
601+
config.ActionCache = &runner.GoGitActionCacheOfflineMode{
602+
Parent: runner.GoGitActionCache{
600603
Path: config.ActionCacheDir,
601-
}
604+
},
602605
}
603-
if len(input.localRepository) > 0 {
604-
localRepositories := map[string]string{}
605-
for _, l := range input.localRepository {
606-
k, v, _ := strings.Cut(l, "=")
607-
localRepositories[k] = v
608-
}
609-
config.ActionCache = &runner.LocalRepositoryCache{
610-
Parent: config.ActionCache,
611-
LocalRepositories: localRepositories,
612-
CacheDirCache: map[string]string{},
613-
}
606+
}
607+
if len(input.localRepository) > 0 {
608+
localRepositories := map[string]string{}
609+
for _, l := range input.localRepository {
610+
k, v, _ := strings.Cut(l, "=")
611+
localRepositories[k] = v
612+
}
613+
config.ActionCache = &runner.LocalRepositoryCache{
614+
Parent: config.ActionCache,
615+
LocalRepositories: localRepositories,
616+
CacheDirCache: map[string]string{},
614617
}
615618
}
619+
616620
r, err := runner.New(config)
617621
if err != nil {
618622
return err

pkg/container/docker_run.go

+3
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,9 @@ func (cr *containerReference) waitForCommand(ctx context.Context, isTerminal boo
699699
}
700700

701701
func (cr *containerReference) CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error {
702+
if common.Dryrun(ctx) {
703+
return nil
704+
}
702705
// Mkdir
703706
buf := &bytes.Buffer{}
704707
tw := tar.NewWriter(buf)

pkg/container/host_environment.go

+3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ func (e *HostEnvironment) Copy(destPath string, files ...*FileEntry) common.Exec
6262
}
6363

6464
func (e *HostEnvironment) CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error {
65+
if common.Dryrun(ctx) {
66+
return nil
67+
}
6568
if err := os.RemoveAll(destPath); err != nil {
6669
return err
6770
}

pkg/runner/action.go

+25-49
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func readActionImpl(ctx context.Context, step *model.Step, actionDir string, act
116116
return action, err
117117
}
118118

119-
func maybeCopyToActionDir(ctx context.Context, step actionStep, actionDir string, actionPath string, containerActionDir string) error {
119+
func maybeCopyToActionDir(ctx context.Context, step actionStep, actionPath string, containerActionDir string) error {
120120
logger := common.Logger(ctx)
121121
rc := step.getRunContext()
122122
stepModel := step.getStepModel()
@@ -133,21 +133,13 @@ func maybeCopyToActionDir(ctx context.Context, step actionStep, actionDir string
133133
containerActionDirCopy += `/`
134134
}
135135

136-
if rc.Config != nil && rc.Config.ActionCache != nil {
137-
raction := step.(*stepActionRemote)
138-
ta, err := rc.Config.ActionCache.GetTarArchive(ctx, raction.cacheDir, raction.resolvedSha, "")
139-
if err != nil {
140-
return err
141-
}
142-
defer ta.Close()
143-
return rc.JobContainer.CopyTarStream(ctx, containerActionDirCopy, ta)
144-
}
145-
146-
if err := removeGitIgnore(ctx, actionDir); err != nil {
136+
raction := step.(*stepActionRemote)
137+
ta, err := rc.getActionCache().GetTarArchive(ctx, raction.cacheDir, raction.resolvedSha, "")
138+
if err != nil {
147139
return err
148140
}
149-
150-
return rc.JobContainer.CopyDir(containerActionDirCopy, actionDir+"/", rc.Config.UseGitIgnore)(ctx)
141+
defer ta.Close()
142+
return rc.JobContainer.CopyTarStream(ctx, containerActionDirCopy, ta)
151143
}
152144

153145
func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction) common.Executor {
@@ -176,7 +168,7 @@ func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction
176168

177169
switch action.Runs.Using {
178170
case model.ActionRunsUsingNode12, model.ActionRunsUsingNode16, model.ActionRunsUsingNode20:
179-
if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil {
171+
if err := maybeCopyToActionDir(ctx, step, actionPath, containerActionDir); err != nil {
180172
return err
181173
}
182174
containerArgs := []string{rc.GetNodeToolFullPath(ctx), path.Join(containerActionDir, action.Runs.Main)}
@@ -186,13 +178,13 @@ func runActionImpl(step actionStep, actionDir string, remoteAction *remoteAction
186178

187179
return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx)
188180
case model.ActionRunsUsingDocker:
189-
location := actionLocation
190181
if remoteAction == nil {
191-
location = containerActionDir
182+
actionDir = ""
183+
actionPath = containerActionDir
192184
}
193-
return execAsDocker(ctx, step, actionName, location, remoteAction == nil, "entrypoint")
185+
return execAsDocker(ctx, step, actionName, actionDir, actionPath, remoteAction == nil, "entrypoint")
194186
case model.ActionRunsUsingComposite:
195-
if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil {
187+
if err := maybeCopyToActionDir(ctx, step, actionPath, containerActionDir); err != nil {
196188
return err
197189
}
198190

@@ -223,27 +215,10 @@ func setupActionEnv(ctx context.Context, step actionStep, _ *remoteAction) error
223215
return nil
224216
}
225217

226-
// https://github.com/nektos/act/issues/228#issuecomment-629709055
227-
// files in .gitignore are not copied in a Docker container
228-
// this causes issues with actions that ignore other important resources
229-
// such as `node_modules` for example
230-
func removeGitIgnore(ctx context.Context, directory string) error {
231-
gitIgnorePath := path.Join(directory, ".gitignore")
232-
if _, err := os.Stat(gitIgnorePath); err == nil {
233-
// .gitignore exists
234-
common.Logger(ctx).Debugf("Removing %s before docker cp", gitIgnorePath)
235-
err := os.Remove(gitIgnorePath)
236-
if err != nil {
237-
return err
238-
}
239-
}
240-
return nil
241-
}
242-
243218
// TODO: break out parts of function to reduce complexicity
244219
//
245220
//nolint:gocyclo
246-
func execAsDocker(ctx context.Context, step actionStep, actionName string, basedir string, localAction bool, entrypointType string) error {
221+
func execAsDocker(ctx context.Context, step actionStep, actionName, basedir, subpath string, localAction bool, entrypointType string) error {
247222
logger := common.Logger(ctx)
248223
rc := step.getRunContext()
249224
action := step.getActionModel()
@@ -260,7 +235,7 @@ func execAsDocker(ctx context.Context, step actionStep, actionName string, based
260235
image = fmt.Sprintf("%s-dockeraction:%s", regexp.MustCompile("[^a-zA-Z0-9]").ReplaceAllString(actionName, "-"), "latest")
261236
image = fmt.Sprintf("act-%s", strings.TrimLeft(image, "-"))
262237
image = strings.ToLower(image)
263-
contextDir, fileName := filepath.Split(filepath.Join(basedir, action.Runs.Image))
238+
contextDir, fileName := path.Split(path.Join(subpath, action.Runs.Image))
264239

265240
anyArchExists, err := container.ImageExistsLocally(ctx, image, "any")
266241
if err != nil {
@@ -291,16 +266,16 @@ func execAsDocker(ctx context.Context, step actionStep, actionName string, based
291266
return err
292267
}
293268
defer buildContext.Close()
294-
} else if rc.Config.ActionCache != nil {
269+
} else {
295270
rstep := step.(*stepActionRemote)
296-
buildContext, err = rc.Config.ActionCache.GetTarArchive(ctx, rstep.cacheDir, rstep.resolvedSha, contextDir)
271+
buildContext, err = rc.getActionCache().GetTarArchive(ctx, rstep.cacheDir, rstep.resolvedSha, contextDir)
297272
if err != nil {
298273
return err
299274
}
300275
defer buildContext.Close()
301276
}
302277
prepImage = container.NewDockerBuildExecutor(container.NewDockerBuildExecutorInput{
303-
ContextDir: contextDir,
278+
ContextDir: filepath.Join(basedir, contextDir),
304279
Dockerfile: fileName,
305280
ImageTag: image,
306281
BuildContext: buildContext,
@@ -324,6 +299,7 @@ func execAsDocker(ctx context.Context, step actionStep, actionName string, based
324299
if len(entrypoint) == 0 {
325300
if entrypointType == "pre-entrypoint" && action.Runs.PreEntrypoint != "" {
326301
entrypoint, err = shellquote.Split(action.Runs.PreEntrypoint)
302+
327303
if err != nil {
328304
return err
329305
}
@@ -545,7 +521,7 @@ func runPreStep(step actionStep) common.Executor {
545521

546522
switch action.Runs.Using {
547523
case model.ActionRunsUsingNode12, model.ActionRunsUsingNode16, model.ActionRunsUsingNode20:
548-
if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil {
524+
if err := maybeCopyToActionDir(ctx, step, actionPath, containerActionDir); err != nil {
549525
return err
550526
}
551527

@@ -557,11 +533,11 @@ func runPreStep(step actionStep) common.Executor {
557533
return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx)
558534

559535
case model.ActionRunsUsingDocker:
560-
location := actionLocation
561536
if remoteAction == nil {
562-
location = containerActionDir
537+
actionDir = ""
538+
actionPath = containerActionDir
563539
}
564-
return execAsDocker(ctx, step, actionName, location, remoteAction == nil, "pre-entrypoint")
540+
return execAsDocker(ctx, step, actionName, actionDir, actionPath, remoteAction == nil, "pre-entrypoint")
565541

566542
case model.ActionRunsUsingComposite:
567543
if step.getCompositeSteps() == nil {
@@ -662,14 +638,14 @@ func runPostStep(step actionStep) common.Executor {
662638
return rc.execJobContainer(containerArgs, *step.getEnv(), "", "")(ctx)
663639

664640
case model.ActionRunsUsingDocker:
665-
location := actionLocation
666641
if remoteAction == nil {
667-
location = containerActionDir
642+
actionDir = ""
643+
actionPath = containerActionDir
668644
}
669-
return execAsDocker(ctx, step, actionName, location, remoteAction == nil, "post-entrypoint")
645+
return execAsDocker(ctx, step, actionName, actionDir, actionPath, remoteAction == nil, "post-entrypoint")
670646

671647
case model.ActionRunsUsingComposite:
672-
if err := maybeCopyToActionDir(ctx, step, actionDir, actionPath, containerActionDir); err != nil {
648+
if err := maybeCopyToActionDir(ctx, step, actionPath, containerActionDir); err != nil {
673649
return err
674650
}
675651

pkg/runner/action_test.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,10 @@ func TestActionRunner(t *testing.T) {
227227
ctx := context.Background()
228228

229229
cm := &containerMock{}
230-
cm.On("CopyDir", "/var/run/act/actions/dir/", "dir/", false).Return(func(_ context.Context) error { return nil })
230+
cm.Mock.On("CopyTarStream", ctx, "/var/run/act/actions/dir/", mock.Anything).Return(nil)
231+
232+
cacheMock := &TestRepositoryCache{}
233+
cacheMock.Mock.On("GetTarArchive", ctx, "", "", "").Return(io.NopCloser(io.MultiReader()))
231234

232235
envMatcher := mock.MatchedBy(func(env map[string]string) bool {
233236
for k, v := range tt.expectedEnv {
@@ -241,6 +244,7 @@ func TestActionRunner(t *testing.T) {
241244
cm.On("Exec", []string{"node", "/var/run/act/actions/dir/path"}, envMatcher, "", "").Return(func(_ context.Context) error { return nil })
242245

243246
tt.step.getRunContext().JobContainer = cm
247+
tt.step.getRunContext().Config.ActionCache = cacheMock
244248

245249
err := runActionImpl(tt.step, "dir", newRemoteAction("org/repo/path@ref"))(ctx)
246250

pkg/runner/container_mock_test.go

+9
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,12 @@ func (cm *containerMock) GetContainerArchive(ctx context.Context, srcPath string
7373
}
7474
return args.Get(0).(io.ReadCloser), err
7575
}
76+
77+
func (cm *containerMock) CopyTarStream(ctx context.Context, destPath string, tarStream io.Reader) error {
78+
args := cm.Mock.Called(ctx, destPath, tarStream)
79+
err, hasErr := args.Get(0).(error)
80+
if !hasErr {
81+
err = nil
82+
}
83+
return err
84+
}

pkg/runner/reusable_workflow.go

+4-50
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,11 @@ package runner
33
import (
44
"archive/tar"
55
"context"
6-
"errors"
76
"fmt"
8-
"io/fs"
9-
"os"
107
"path"
118
"regexp"
12-
"sync"
139

1410
"github.com/nektos/act/pkg/common"
15-
"github.com/nektos/act/pkg/common/git"
1611
"github.com/nektos/act/pkg/model"
1712
)
1813

@@ -32,27 +27,20 @@ func newRemoteReusableWorkflowExecutor(rc *RunContext) common.Executor {
3227
// instead we will just use {owner}-{repo}@{ref} as our target directory. This should also improve performance when we are using
3328
// multiple reusable workflows from the same repository and ref since for each workflow we won't have to clone it again
3429
filename := fmt.Sprintf("%s/%s@%s", remoteReusableWorkflow.Org, remoteReusableWorkflow.Repo, remoteReusableWorkflow.Ref)
35-
workflowDir := fmt.Sprintf("%s/%s", rc.ActionCacheDir(), safeFilename(filename))
3630

37-
if rc.Config.ActionCache != nil {
38-
return newActionCacheReusableWorkflowExecutor(rc, filename, remoteReusableWorkflow)
39-
}
40-
41-
return common.NewPipelineExecutor(
42-
newMutexExecutor(cloneIfRequired(rc, *remoteReusableWorkflow, workflowDir)),
43-
newReusableWorkflowExecutor(rc, workflowDir, fmt.Sprintf("./.github/workflows/%s", remoteReusableWorkflow.Filename)),
44-
)
31+
return newActionCacheReusableWorkflowExecutor(rc, filename, remoteReusableWorkflow)
4532
}
4633

4734
func newActionCacheReusableWorkflowExecutor(rc *RunContext, filename string, remoteReusableWorkflow *remoteReusableWorkflow) common.Executor {
4835
return func(ctx context.Context) error {
4936
ghctx := rc.getGithubContext(ctx)
5037
remoteReusableWorkflow.URL = ghctx.ServerURL
51-
sha, err := rc.Config.ActionCache.Fetch(ctx, filename, remoteReusableWorkflow.CloneURL(), remoteReusableWorkflow.Ref, ghctx.Token)
38+
cache := rc.getActionCache()
39+
sha, err := cache.Fetch(ctx, filename, remoteReusableWorkflow.CloneURL(), remoteReusableWorkflow.Ref, ghctx.Token)
5240
if err != nil {
5341
return err
5442
}
55-
archive, err := rc.Config.ActionCache.GetTarArchive(ctx, filename, sha, fmt.Sprintf(".github/workflows/%s", remoteReusableWorkflow.Filename))
43+
archive, err := cache.GetTarArchive(ctx, filename, sha, fmt.Sprintf(".github/workflows/%s", remoteReusableWorkflow.Filename))
5644
if err != nil {
5745
return err
5846
}
@@ -79,40 +67,6 @@ func newActionCacheReusableWorkflowExecutor(rc *RunContext, filename string, rem
7967
}
8068
}
8169

82-
var (
83-
executorLock sync.Mutex
84-
)
85-
86-
func newMutexExecutor(executor common.Executor) common.Executor {
87-
return func(ctx context.Context) error {
88-
executorLock.Lock()
89-
defer executorLock.Unlock()
90-
91-
return executor(ctx)
92-
}
93-
}
94-
95-
func cloneIfRequired(rc *RunContext, remoteReusableWorkflow remoteReusableWorkflow, targetDirectory string) common.Executor {
96-
return common.NewConditionalExecutor(
97-
func(_ context.Context) bool {
98-
_, err := os.Stat(targetDirectory)
99-
notExists := errors.Is(err, fs.ErrNotExist)
100-
return notExists
101-
},
102-
func(ctx context.Context) error {
103-
remoteReusableWorkflow.URL = rc.getGithubContext(ctx).ServerURL
104-
return git.NewGitCloneExecutor(git.NewGitCloneExecutorInput{
105-
URL: remoteReusableWorkflow.CloneURL(),
106-
Ref: remoteReusableWorkflow.Ref,
107-
Dir: targetDirectory,
108-
Token: rc.Config.Token,
109-
OfflineMode: rc.Config.ActionOfflineMode,
110-
})(ctx)
111-
},
112-
nil,
113-
)
114-
}
115-
11670
func newReusableWorkflowExecutor(rc *RunContext, directory string, workflow string) common.Executor {
11771
return func(ctx context.Context) error {
11872
planner, err := model.NewWorkflowPlanner(path.Join(directory, workflow), true)

0 commit comments

Comments
 (0)