2022-08-23 15:34:47 +03:00
package runtime
import (
2022-09-25 13:54:00 +03:00
"bytes"
2022-08-23 15:34:47 +03:00
"context"
2022-09-25 13:54:00 +03:00
"encoding/json"
2022-08-23 15:34:47 +03:00
"fmt"
"os"
"path/filepath"
2022-10-29 07:26:27 +03:00
"sync"
2022-08-23 15:34:47 +03:00
2022-09-03 10:57:53 +03:00
"gitea.com/gitea/act_runner/client"
2022-09-03 15:57:32 +03:00
runnerv1 "gitea.com/gitea/proto-go/runner/v1"
2022-08-23 15:34:47 +03:00
"github.com/nektos/act/pkg/artifacts"
"github.com/nektos/act/pkg/common"
"github.com/nektos/act/pkg/model"
"github.com/nektos/act/pkg/runner"
log "github.com/sirupsen/logrus"
)
2022-10-29 07:26:27 +03:00
var globalTaskMap sync . Map
2022-08-23 15:34:47 +03:00
type TaskInput struct {
repoDirectory string
2022-10-29 07:26:27 +03:00
// actor string
2022-08-23 15:34:47 +03:00
// workdir string
// workflowsPath string
// autodetectEvent bool
// eventPath string
reuseContainers bool
bindWorkdir bool
// secrets []string
// envs []string
// platforms []string
// dryrun bool
forcePull bool
forceRebuild bool
// noOutput bool
// envfile string
// secretfile string
insecureSecrets bool
// defaultBranch string
privileged bool
usernsMode string
containerArchitecture string
containerDaemonSocket string
// noWorkflowRecurse bool
useGitIgnore bool
containerCapAdd [ ] string
containerCapDrop [ ] string
autoRemove bool
artifactServerPath string
artifactServerPort string
jsonLogger bool
noSkipCheckout bool
// remoteName string
ForgeInstance string
EnvFile string
}
type Task struct {
2022-09-03 10:57:53 +03:00
BuildID int64
2022-08-23 15:34:47 +03:00
Input * TaskInput
2022-09-03 10:57:53 +03:00
2022-09-25 13:54:00 +03:00
client client . Client
log * log . Entry
2022-08-23 15:34:47 +03:00
}
2022-09-25 13:54:00 +03:00
// NewTask creates a new task
2022-10-16 18:51:53 +03:00
func NewTask ( forgeInstance string , buildID int64 , client client . Client ) * Task {
2022-08-23 15:34:47 +03:00
task := & Task {
Input : & TaskInput {
reuseContainers : true ,
2022-10-16 18:51:53 +03:00
ForgeInstance : forgeInstance ,
2022-08-23 15:34:47 +03:00
} ,
2022-09-03 10:57:53 +03:00
BuildID : buildID ,
2022-09-25 13:54:00 +03:00
client : client ,
log : log . WithField ( "buildID" , buildID ) ,
2022-08-23 15:34:47 +03:00
}
task . Input . repoDirectory , _ = os . Getwd ( )
return task
}
// getWorkflowsPath return the workflows directory, it will try .gitea first and then fallback to .github
func getWorkflowsPath ( dir string ) ( string , error ) {
p := filepath . Join ( dir , ".gitea/workflows" )
_ , err := os . Stat ( p )
if err != nil {
if ! os . IsNotExist ( err ) {
return "" , err
}
return filepath . Join ( dir , ".github/workflows" ) , nil
}
return p , nil
}
func demoPlatforms ( ) map [ string ] string {
return map [ string ] string {
"ubuntu-latest" : "node:16-buster-slim" ,
"ubuntu-20.04" : "node:16-buster-slim" ,
"ubuntu-18.04" : "node:16-buster-slim" ,
}
}
2022-10-31 10:31:42 +03:00
func getToken ( task * runnerv1 . Task ) string {
token := task . Secrets [ "GITHUB_TOKEN" ]
if task . Secrets [ "GITEA_TOKEN" ] != "" {
token = task . Secrets [ "GITEA_TOKEN" ]
}
if task . Context . Fields [ "token" ] . GetStringValue ( ) != "" {
token = task . Context . Fields [ "token" ] . GetStringValue ( )
}
return token
}
2022-09-25 13:54:00 +03:00
func ( t * Task ) Run ( ctx context . Context , task * runnerv1 . Task ) error {
2022-10-29 07:26:27 +03:00
ctx , cancel := context . WithCancel ( ctx )
defer cancel ( )
2022-09-25 13:54:00 +03:00
_ , exist := globalTaskMap . Load ( task . Id )
2022-09-04 10:44:29 +03:00
if exist {
2022-09-25 13:54:00 +03:00
return fmt . Errorf ( "task %d already exists" , task . Id )
2022-09-04 10:44:29 +03:00
}
// set task ve to global map
// when task is done or canceled, it will be removed from the map
2022-09-25 13:54:00 +03:00
globalTaskMap . Store ( task . Id , t )
defer globalTaskMap . Delete ( task . Id )
lastWords := ""
2022-10-29 07:26:27 +03:00
reporter := NewReporter ( ctx , cancel , t . client , task . Id )
2022-09-25 13:54:00 +03:00
defer func ( ) {
_ = reporter . Close ( lastWords )
} ( )
reporter . Logf ( "received task %v of job %v" , task . Id , task . Context . Fields [ "job" ] . GetStringValue ( ) )
2022-09-04 10:44:29 +03:00
2022-08-23 15:34:47 +03:00
workflowsPath , err := getWorkflowsPath ( t . Input . repoDirectory )
if err != nil {
2022-09-25 13:54:00 +03:00
lastWords = err . Error ( )
2022-09-04 10:44:29 +03:00
return err
2022-08-23 15:34:47 +03:00
}
2022-09-03 10:57:53 +03:00
t . log . Debugf ( "workflows path: %s" , workflowsPath )
2022-09-25 13:54:00 +03:00
workflow , err := model . ReadWorkflow ( bytes . NewReader ( task . WorkflowPayload ) )
2022-08-23 15:34:47 +03:00
if err != nil {
2022-09-25 13:54:00 +03:00
lastWords = err . Error ( )
2022-09-04 10:44:29 +03:00
return err
2022-08-23 15:34:47 +03:00
}
var plan * model . Plan
2022-10-29 07:26:27 +03:00
jobIDs := workflow . GetJobIDs ( )
if len ( jobIDs ) != 1 {
err := fmt . Errorf ( "multiple jobs found: %v" , jobIDs )
2022-09-25 13:54:00 +03:00
lastWords = err . Error ( )
return err
2022-08-23 15:34:47 +03:00
}
2022-10-29 07:26:27 +03:00
jobID := jobIDs [ 0 ]
plan = model . CombineWorkflowPlanner ( workflow ) . PlanJob ( jobID )
job := workflow . GetJob ( jobID )
reporter . ResetSteps ( len ( job . Steps ) )
2022-08-23 15:34:47 +03:00
2022-09-25 13:54:00 +03:00
log . Infof ( "plan: %+v" , plan . Stages [ 0 ] . Runs )
2022-08-23 15:34:47 +03:00
curDir , err := os . Getwd ( )
if err != nil {
2022-09-25 13:54:00 +03:00
lastWords = err . Error ( )
return err
}
2022-10-31 10:31:42 +03:00
token := getToken ( task )
log . Infof ( "task %v token is %v" , task . Id , token )
2022-09-25 13:54:00 +03:00
dataContext := task . Context . Fields
preset := & model . GithubContext {
Event : dataContext [ "event" ] . GetStructValue ( ) . AsMap ( ) ,
RunID : dataContext [ "run_id" ] . GetStringValue ( ) ,
RunNumber : dataContext [ "run_number" ] . GetStringValue ( ) ,
Actor : dataContext [ "actor" ] . GetStringValue ( ) ,
Repository : dataContext [ "repository" ] . GetStringValue ( ) ,
EventName : dataContext [ "event_name" ] . GetStringValue ( ) ,
Sha : dataContext [ "sha" ] . GetStringValue ( ) ,
Ref : dataContext [ "ref" ] . GetStringValue ( ) ,
RefName : dataContext [ "ref_name" ] . GetStringValue ( ) ,
RefType : dataContext [ "ref_type" ] . GetStringValue ( ) ,
HeadRef : dataContext [ "head_ref" ] . GetStringValue ( ) ,
BaseRef : dataContext [ "base_ref" ] . GetStringValue ( ) ,
2022-10-31 10:31:42 +03:00
Token : token ,
2022-09-25 13:54:00 +03:00
RepositoryOwner : dataContext [ "repository_owner" ] . GetStringValue ( ) ,
RetentionDays : dataContext [ "retention_days" ] . GetStringValue ( ) ,
}
eventJSON , err := json . Marshal ( preset . Event )
if err != nil {
lastWords = err . Error ( )
2022-09-04 10:44:29 +03:00
return err
2022-08-23 15:34:47 +03:00
}
input := t . Input
config := & runner . Config {
2022-09-25 13:54:00 +03:00
Workdir : curDir , // TODO: temp dir?
BindWorkdir : input . bindWorkdir ,
ReuseContainers : input . reuseContainers ,
ForcePull : input . forcePull ,
ForceRebuild : input . forceRebuild ,
LogOutput : true ,
JSONLogger : input . jsonLogger ,
Secrets : task . Secrets ,
2022-08-23 15:34:47 +03:00
InsecureSecrets : input . insecureSecrets ,
2022-09-25 13:54:00 +03:00
Platforms : demoPlatforms ( ) , // TODO: supported platforms
2022-08-23 15:34:47 +03:00
Privileged : input . privileged ,
UsernsMode : input . usernsMode ,
ContainerArchitecture : input . containerArchitecture ,
ContainerDaemonSocket : input . containerDaemonSocket ,
UseGitIgnore : input . useGitIgnore ,
GitHubInstance : input . ForgeInstance ,
ContainerCapAdd : input . containerCapAdd ,
ContainerCapDrop : input . containerCapDrop ,
AutoRemove : input . autoRemove ,
ArtifactServerPath : input . artifactServerPath ,
ArtifactServerPort : input . artifactServerPort ,
NoSkipCheckout : input . noSkipCheckout ,
2022-09-25 13:54:00 +03:00
PresetGitHubContext : preset ,
EventJSON : string ( eventJSON ) ,
2022-08-23 15:34:47 +03:00
}
r , err := runner . New ( config )
if err != nil {
2022-09-25 13:54:00 +03:00
lastWords = err . Error ( )
2022-09-04 10:44:29 +03:00
return err
2022-08-23 15:34:47 +03:00
}
2022-10-29 07:26:27 +03:00
artifactCancel := artifacts . Serve ( ctx , input . artifactServerPath , input . artifactServerPort )
2022-09-03 10:57:53 +03:00
t . log . Debugf ( "artifacts server started at %s:%s" , input . artifactServerPath , input . artifactServerPort )
2022-08-23 15:34:47 +03:00
executor := r . NewPlanExecutor ( plan ) . Finally ( func ( ctx context . Context ) error {
2022-10-29 07:26:27 +03:00
artifactCancel ( )
2022-08-23 15:34:47 +03:00
return nil
} )
2022-09-03 10:57:53 +03:00
t . log . Infof ( "workflow prepared" )
2022-09-25 13:54:00 +03:00
reporter . Logf ( "workflow prepared" )
2022-09-03 10:57:53 +03:00
// add logger recorders
2022-09-25 13:54:00 +03:00
ctx = common . WithLoggerHook ( ctx , reporter )
2022-09-03 10:57:53 +03:00
2022-08-23 15:34:47 +03:00
if err := executor ( ctx ) ; err != nil {
2022-09-25 13:54:00 +03:00
lastWords = err . Error ( )
2022-09-04 10:44:29 +03:00
return err
2022-08-23 15:34:47 +03:00
}
2022-09-03 10:57:53 +03:00
2022-09-04 10:44:29 +03:00
return nil
2022-08-23 15:34:47 +03:00
}