From dca7801682f96b8ddfa1052704654ab9459ba437 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Wed, 15 Feb 2023 16:28:33 +0800 Subject: [PATCH] Support uses http(s)://host/owner/repo as actions (#14) Examples: ```yaml jobs: my_first_job: steps: - name: My first step uses: https://gitea.com/actions/heroku@main - name: My second step uses: http://example.com/actions/heroku@v2.0.1 ``` Reviewed-on: https://gitea.com/gitea/act/pulls/14 Reviewed-by: Lunny Xiao Co-authored-by: Jason Song Co-committed-by: Jason Song --- pkg/runner/step_action_remote.go | 31 +++++++-- pkg/runner/step_action_remote_test.go | 94 +++++++++++++++++++++++++++ 2 files changed, 120 insertions(+), 5 deletions(-) diff --git a/pkg/runner/step_action_remote.go b/pkg/runner/step_action_remote.go index 264b060..8011d68 100644 --- a/pkg/runner/step_action_remote.go +++ b/pkg/runner/step_action_remote.go @@ -44,8 +44,6 @@ func (sar *stepActionRemote) prepareActionExecutor() common.Executor { return fmt.Errorf("Expected format {org}/{repo}[/path]@ref. Actual '%s' Input string was not in a correct format", sar.Step.Uses) } - sar.remoteAction.URL = sar.RunContext.Config.DefaultActionInstance - github := sar.getGithubContext(ctx) if sar.remoteAction.IsCheckout() && isLocalCheckout(github, sar.Step) && !sar.RunContext.Config.NoSkipCheckout { common.Logger(ctx).Debugf("Skipping local actions/checkout because workdir was already copied") @@ -61,7 +59,7 @@ func (sar *stepActionRemote) prepareActionExecutor() common.Executor { actionDir := fmt.Sprintf("%s/%s", sar.RunContext.ActionCacheDir(), strings.ReplaceAll(sar.Step.Uses, "/", "-")) gitClone := stepActionRemoteNewCloneExecutor(git.NewGitCloneExecutorInput{ - URL: sar.remoteAction.CloneURL(), + URL: sar.remoteAction.CloneURL(sar.RunContext.Config.DefaultActionInstance), Ref: sar.remoteAction.Ref, Dir: actionDir, Token: "", /* @@ -215,8 +213,11 @@ type remoteAction struct { Ref string } -func (ra *remoteAction) CloneURL() string { +func (ra *remoteAction) CloneURL(defaultURL string) string { u := ra.URL + if u == "" { + u = defaultURL + } if !strings.HasPrefix(u, "http://") && !strings.HasPrefix(u, "https://") { u = "https://" + u } @@ -231,6 +232,26 @@ func (ra *remoteAction) IsCheckout() bool { } func newRemoteAction(action string) *remoteAction { + // support http(s)://host/owner/repo@v3 + for _, schema := range []string{"https://", "http://"} { + if strings.HasPrefix(action, schema) { + splits := strings.SplitN(strings.TrimPrefix(action, schema), "/", 2) + if len(splits) != 2 { + return nil + } + ret := parseAction(splits[1]) + if ret == nil { + return nil + } + ret.URL = schema + splits[0] + return ret + } + } + + return parseAction(action) +} + +func parseAction(action string) *remoteAction { // GitHub's document[^] describes: // > We strongly recommend that you include the version of // > the action you are using by specifying a Git ref, SHA, or Docker tag number. @@ -246,6 +267,6 @@ func newRemoteAction(action string) *remoteAction { Repo: matches[2], Path: matches[4], Ref: matches[6], - URL: "github.com", + URL: "", } } diff --git a/pkg/runner/step_action_remote_test.go b/pkg/runner/step_action_remote_test.go index 72b0eee..abcc8e7 100644 --- a/pkg/runner/step_action_remote_test.go +++ b/pkg/runner/step_action_remote_test.go @@ -623,3 +623,97 @@ func TestStepActionRemotePost(t *testing.T) { }) } } + +func Test_newRemoteAction(t *testing.T) { + tests := []struct { + action string + want *remoteAction + wantCloneURL string + }{ + { + action: "actions/heroku@main", + want: &remoteAction{ + URL: "", + Org: "actions", + Repo: "heroku", + Path: "", + Ref: "main", + }, + wantCloneURL: "https://github.com/actions/heroku", + }, + { + action: "actions/aws/ec2@main", + want: &remoteAction{ + URL: "", + Org: "actions", + Repo: "aws", + Path: "ec2", + Ref: "main", + }, + wantCloneURL: "https://github.com/actions/aws", + }, + { + action: "./.github/actions/my-action", // it's valid for GitHub, but act don't support it + want: nil, + }, + { + action: "docker://alpine:3.8", // it's valid for GitHub, but act don't support it + want: nil, + }, + { + action: "https://gitea.com/actions/heroku@main", // it's invalid for GitHub, but gitea supports it + want: &remoteAction{ + URL: "https://gitea.com", + Org: "actions", + Repo: "heroku", + Path: "", + Ref: "main", + }, + wantCloneURL: "https://gitea.com/actions/heroku", + }, + { + action: "https://gitea.com/actions/aws/ec2@main", // it's invalid for GitHub, but gitea supports it + want: &remoteAction{ + URL: "https://gitea.com", + Org: "actions", + Repo: "aws", + Path: "ec2", + Ref: "main", + }, + wantCloneURL: "https://gitea.com/actions/aws", + }, + { + action: "http://gitea.com/actions/heroku@main", // it's invalid for GitHub, but gitea supports it + want: &remoteAction{ + URL: "http://gitea.com", + Org: "actions", + Repo: "heroku", + Path: "", + Ref: "main", + }, + wantCloneURL: "http://gitea.com/actions/heroku", + }, + { + action: "http://gitea.com/actions/aws/ec2@main", // it's invalid for GitHub, but gitea supports it + want: &remoteAction{ + URL: "http://gitea.com", + Org: "actions", + Repo: "aws", + Path: "ec2", + Ref: "main", + }, + wantCloneURL: "http://gitea.com/actions/aws", + }, + } + for _, tt := range tests { + t.Run(tt.action, func(t *testing.T) { + got := newRemoteAction(tt.action) + assert.Equalf(t, tt.want, got, "newRemoteAction(%v)", tt.action) + cloneURL := "" + if got != nil { + cloneURL = got.CloneURL("github.com") + } + assert.Equalf(t, tt.wantCloneURL, cloneURL, "newRemoteAction(%v).CloneURL()", tt.action) + }) + } +}