diff --git a/act/model/workflow.go b/act/model/workflow.go index 815124bb..f4861041 100644 --- a/act/model/workflow.go +++ b/act/model/workflow.go @@ -1,9 +1,12 @@ package model import ( + "crypto/sha256" + "encoding/hex" "errors" "fmt" "io" + "path/filepath" "reflect" "regexp" "strconv" @@ -734,6 +737,12 @@ func (s *Step) Type() StepType { return StepTypeUsesActionRemote } +func (s *Step) UsesHash() string { + hashBytes := sha256.Sum256([]byte(s.Uses)) + hashString := hex.EncodeToString(hashBytes[:]) + return filepath.Join(hashString[:2], hashString[2:]) +} + // ReadWorkflow returns a list of jobs for a given workflow file reader func ReadWorkflow(in io.Reader, validate bool) (*Workflow, error) { if validate { diff --git a/act/model/workflow_test.go b/act/model/workflow_test.go index adb61d1b..d3ea7234 100644 --- a/act/model/workflow_test.go +++ b/act/model/workflow_test.go @@ -668,3 +668,37 @@ func TestReadWorkflow_WorkflowDispatchConfig(t *testing.T) { Type: "choice", }, workflowDispatch.Inputs["logLevel"]) } + +func TestStepUsesHash(t *testing.T) { + type fields struct { + Uses string + } + tests := []struct { + name string + fields fields + want string + }{ + { + name: "regular", + fields: fields{ + Uses: "https://example.com/testa/testb@v3", + }, + want: "e6/d70c1e8a4cc1e1cb02e32b3b60cc8dff319bb4fe5832fbc8b800711f18e7a2", + }, + { + name: "empty", + fields: fields{ + Uses: "", + }, + want: "e3/b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := &Step{ + Uses: tt.fields.Uses, + } + assert.Equalf(t, tt.want, s.UsesHash(), "UsesHash()") + }) + } +} diff --git a/act/runner/action.go b/act/runner/action.go index c5ed37ed..9ddbf5b4 100644 --- a/act/runner/action.go +++ b/act/runner/action.go @@ -551,7 +551,7 @@ func runPreStep(step actionStep) common.Executor { var actionPath string if _, ok := step.(*stepActionRemote); ok { actionPath = newRemoteAction(stepModel.Uses).Path - actionDir = fmt.Sprintf("%s/%s", rc.ActionCacheDir(), safeFilename(stepModel.Uses)) + actionDir = filepath.Join(rc.ActionCacheDir(), stepModel.UsesHash()) } else { actionDir = filepath.Join(rc.Config.Workdir, stepModel.Uses) actionPath = "" @@ -602,7 +602,7 @@ func runPreStep(step actionStep) common.Executor { var actionPath string if _, ok := step.(*stepActionRemote); ok { actionPath = newRemoteAction(stepModel.Uses).Path - actionDir = fmt.Sprintf("%s/%s", rc.ActionCacheDir(), safeFilename(stepModel.Uses)) + actionDir = filepath.Join(rc.ActionCacheDir(), stepModel.UsesHash()) } else { actionDir = filepath.Join(rc.Config.Workdir, stepModel.Uses) actionPath = "" @@ -689,7 +689,7 @@ func runPostStep(step actionStep) common.Executor { var actionPath string if _, ok := step.(*stepActionRemote); ok { actionPath = newRemoteAction(stepModel.Uses).Path - actionDir = fmt.Sprintf("%s/%s", rc.ActionCacheDir(), safeFilename(stepModel.Uses)) + actionDir = filepath.Join(rc.ActionCacheDir(), stepModel.UsesHash()) } else { actionDir = filepath.Join(rc.Config.Workdir, stepModel.Uses) actionPath = "" diff --git a/act/runner/step_action_remote.go b/act/runner/step_action_remote.go index ed4d94ef..947f4992 100644 --- a/act/runner/step_action_remote.go +++ b/act/runner/step_action_remote.go @@ -108,7 +108,7 @@ func (sar *stepActionRemote) prepareActionExecutor() common.Executor { return err } - actionDir := fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), safeFilename(sar.Step.Uses)) + actionDir := filepath.Join(sar.RunContext.ActionCacheDir(), sar.Step.UsesHash()) gitClone := stepActionRemoteNewCloneExecutor(git.NewGitCloneExecutorInput{ URL: sar.remoteAction.CloneURL(sar.RunContext.Config.DefaultActionInstance), Ref: sar.remoteAction.Ref, @@ -177,7 +177,7 @@ func (sar *stepActionRemote) main() common.Executor { return sar.RunContext.JobContainer.CopyDir(copyToPath, sar.RunContext.Config.Workdir+string(filepath.Separator)+".", sar.RunContext.Config.UseGitIgnore)(ctx) } - actionDir := fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), safeFilename(sar.Step.Uses)) + actionDir := filepath.Join(sar.RunContext.ActionCacheDir(), sar.Step.UsesHash()) return sar.runAction(sar, actionDir, sar.remoteAction)(ctx) }), @@ -236,7 +236,7 @@ func (sar *stepActionRemote) getActionModel() *model.Action { func (sar *stepActionRemote) getCompositeRunContext(ctx context.Context) *RunContext { if sar.compositeRunContext == nil { - actionDir := fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), safeFilename(sar.Step.Uses)) + actionDir := filepath.Join(sar.RunContext.ActionCacheDir(), sar.Step.UsesHash()) actionLocation := path.Join(actionDir, sar.remoteAction.Path) _, containerActionDir := getContainerActionPaths(sar.getStepModel(), actionLocation, sar.RunContext)