2022-03-22 16:13:00 -05:00
package runner
import (
"context"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/model"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus"
)
type stepActionRemote struct {
Step * model . Step
RunContext * RunContext
readAction readAction
runAction runAction
action * model . Action
env map [ string ] string
}
func ( sar * stepActionRemote ) pre ( ) common . Executor {
return func ( ctx context . Context ) error {
return nil
}
}
var (
stepActionRemoteNewCloneExecutor = common . NewGitCloneExecutor
)
func ( sar * stepActionRemote ) main ( ) common . Executor {
sar . env = map [ string ] string { }
return runStepExecutor ( sar , func ( ctx context . Context ) error {
remoteAction := newRemoteAction ( sar . Step . Uses )
if remoteAction == nil {
return fmt . Errorf ( "Expected format {org}/{repo}[/path]@ref. Actual '%s' Input string was not in a correct format" , sar . Step . Uses )
}
remoteAction . URL = sar . RunContext . Config . GitHubInstance
github := sar . RunContext . getGithubContext ( )
if remoteAction . IsCheckout ( ) && isLocalCheckout ( github , sar . Step ) && ! sar . RunContext . Config . NoSkipCheckout {
common . Logger ( ctx ) . Debugf ( "Skipping local actions/checkout because workdir was already copied" )
return nil
}
actionDir := fmt . Sprintf ( "%s/%s" , sar . RunContext . ActionCacheDir ( ) , strings . ReplaceAll ( sar . Step . Uses , "/" , "-" ) )
gitClone := stepActionRemoteNewCloneExecutor ( common . NewGitCloneExecutorInput {
URL : remoteAction . CloneURL ( ) ,
Ref : remoteAction . Ref ,
Dir : actionDir ,
Token : github . Token ,
} )
var ntErr common . Executor
if err := gitClone ( ctx ) ; err != nil {
if err . Error ( ) == "short SHA references are not supported" {
err = errors . Cause ( err )
return fmt . Errorf ( "Unable to resolve action `%s`, the provided ref `%s` is the shortened version of a commit SHA, which is not supported. Please use the full commit SHA `%s` instead" , sar . Step . Uses , remoteAction . Ref , err . Error ( ) )
} else if err . Error ( ) != "some refs were not updated" {
return err
} else {
ntErr = common . NewInfoExecutor ( "Non-terminating error while running 'git clone': %v" , err )
}
}
remoteReader := func ( ctx context . Context ) actionYamlReader {
return func ( filename string ) ( io . Reader , io . Closer , error ) {
f , err := os . Open ( filepath . Join ( actionDir , remoteAction . Path , filename ) )
return f , f , err
}
}
return common . NewPipelineExecutor (
ntErr ,
func ( ctx context . Context ) error {
actionModel , err := sar . readAction ( sar . Step , actionDir , remoteAction . Path , remoteReader ( ctx ) , ioutil . WriteFile )
sar . action = actionModel
log . Debugf ( "Read action %v from '%s'" , sar . action , "Unknown" )
return err
} ,
2022-03-29 12:42:11 -05:00
sar . runAction ( sar , actionDir , remoteAction ) ,
2022-03-22 16:13:00 -05:00
) ( ctx )
} )
}
func ( sar * stepActionRemote ) post ( ) common . Executor {
return func ( ctx context . Context ) error {
return nil
}
}
func ( sar * stepActionRemote ) getRunContext ( ) * RunContext {
return sar . RunContext
}
func ( sar * stepActionRemote ) getStepModel ( ) * model . Step {
return sar . Step
}
func ( sar * stepActionRemote ) getEnv ( ) * map [ string ] string {
return & sar . env
}
func ( sar * stepActionRemote ) getActionModel ( ) * model . Action {
return sar . action
}
type remoteAction struct {
URL string
Org string
Repo string
Path string
Ref string
}
func ( ra * remoteAction ) CloneURL ( ) string {
return fmt . Sprintf ( "https://%s/%s/%s" , ra . URL , ra . Org , ra . Repo )
}
func ( ra * remoteAction ) IsCheckout ( ) bool {
if ra . Org == "actions" && ra . Repo == "checkout" {
return true
}
return false
}
func newRemoteAction ( 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.
// Actually, the workflow stops if there is the uses directive that hasn't @ref.
// [^]: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions
r := regexp . MustCompile ( ` ^([^/@]+)/([^/@]+)(/([^@]*))?(@(.*))?$ ` )
matches := r . FindStringSubmatch ( action )
if len ( matches ) < 7 || matches [ 6 ] == "" {
return nil
}
return & remoteAction {
Org : matches [ 1 ] ,
Repo : matches [ 2 ] ,
Path : matches [ 4 ] ,
Ref : matches [ 6 ] ,
URL : "github.com" ,
}
}