2023-02-28 10:44:46 +00:00
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
2022-08-23 12:34:47 +00:00
package runtime
import (
2022-09-25 10:54:00 +00:00
"bytes"
2022-08-23 12:34:47 +00:00
"context"
2022-09-25 10:54:00 +00:00
"encoding/json"
2022-08-23 12:34:47 +00:00
"fmt"
"os"
"path/filepath"
2022-10-29 04:26:27 +00:00
"sync"
2022-11-18 08:12:21 +00:00
"time"
2022-08-23 12:34:47 +00:00
2022-12-06 08:37:38 +00:00
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
2022-08-23 12:34:47 +00: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"
2023-02-28 15:39:30 +00:00
"gitea.com/gitea/act_runner/client"
2022-08-23 12:34:47 +00:00
)
2022-10-29 04:26:27 +00:00
var globalTaskMap sync . Map
2022-08-23 12:34:47 +00:00
type TaskInput struct {
repoDirectory string
2022-10-29 04:26:27 +00:00
// actor string
2022-08-23 12:34:47 +00:00
// workdir string
// workflowsPath string
// autodetectEvent bool
// eventPath string
2022-11-18 08:12:21 +00:00
// reuseContainers bool
// bindWorkdir bool
2022-08-23 12:34:47 +00:00
// secrets []string
2022-11-04 09:23:59 +00:00
envs map [ string ] string
2022-08-23 12:34:47 +00:00
// 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
2022-11-18 08:12:21 +00:00
useGitIgnore bool
containerCapAdd [ ] string
containerCapDrop [ ] string
// autoRemove bool
2022-08-23 12:34:47 +00:00
artifactServerPath string
artifactServerPort string
jsonLogger bool
2022-11-18 08:12:21 +00:00
// noSkipCheckout bool
2022-08-23 12:34:47 +00:00
// remoteName string
2022-11-16 10:08:12 +00:00
EnvFile string
2022-11-18 08:12:21 +00:00
containerNetworkMode string
2022-08-23 12:34:47 +00:00
}
type Task struct {
2022-09-03 07:57:53 +00:00
BuildID int64
2022-08-23 12:34:47 +00:00
Input * TaskInput
2022-09-03 07:57:53 +00:00
2022-11-24 03:55:52 +00:00
client client . Client
log * log . Entry
platformPicker func ( [ ] string ) string
2022-08-23 12:34:47 +00:00
}
2022-09-25 10:54:00 +00:00
// NewTask creates a new task
2022-11-24 03:55:52 +00:00
func NewTask ( forgeInstance string , buildID int64 , client client . Client , runnerEnvs map [ string ] string , picker func ( [ ] string ) string ) * Task {
2022-08-23 12:34:47 +00:00
task := & Task {
Input : & TaskInput {
2022-11-18 08:12:21 +00:00
envs : runnerEnvs ,
containerNetworkMode : "bridge" , // TODO should be configurable
2022-08-23 12:34:47 +00:00
} ,
2022-09-03 07:57:53 +00:00
BuildID : buildID ,
2022-11-24 03:55:52 +00:00
client : client ,
log : log . WithField ( "buildID" , buildID ) ,
platformPicker : picker ,
2022-08-23 12:34:47 +00: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
}
2022-10-31 07:31:42 +00: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
}
2023-03-13 10:57:35 +00:00
func ( t * Task ) Run ( ctx context . Context , task * runnerv1 . Task , runnerName , runnerVersion string ) ( lastErr error ) {
2022-10-29 04:26:27 +00:00
ctx , cancel := context . WithCancel ( ctx )
defer cancel ( )
2022-09-25 10:54:00 +00:00
_ , exist := globalTaskMap . Load ( task . Id )
2022-09-04 07:44:29 +00:00
if exist {
2022-09-25 10:54:00 +00:00
return fmt . Errorf ( "task %d already exists" , task . Id )
2022-09-04 07:44:29 +00:00
}
// set task ve to global map
// when task is done or canceled, it will be removed from the map
2022-09-25 10:54:00 +00:00
globalTaskMap . Store ( task . Id , t )
defer globalTaskMap . Delete ( task . Id )
lastWords := ""
2022-11-02 04:12:45 +00:00
reporter := NewReporter ( ctx , cancel , t . client , task )
2022-09-25 10:54:00 +00:00
defer func ( ) {
2023-01-27 12:18:12 +00:00
// set the job to failed on an error return value
if lastErr != nil {
reporter . Fire ( & log . Entry {
Data : log . Fields {
"jobResult" : "failure" ,
} ,
2023-03-08 13:32:54 +00:00
Time : time . Now ( ) ,
2023-01-27 12:18:12 +00:00
} )
}
2022-09-25 10:54:00 +00:00
_ = reporter . Close ( lastWords )
} ( )
2022-11-01 10:25:41 +00:00
reporter . RunDaemon ( )
2022-09-25 10:54:00 +00:00
2023-03-15 01:44:13 +00:00
reporter . Logf ( "%s(version:%s) received task %v of job %v, be triggered by event: %s" , runnerName , runnerVersion , task . Id , task . Context . Fields [ "job" ] . GetStringValue ( ) , task . Context . Fields [ "event_name" ] . GetStringValue ( ) )
2022-09-04 07:44:29 +00:00
2022-08-23 12:34:47 +00:00
workflowsPath , err := getWorkflowsPath ( t . Input . repoDirectory )
if err != nil {
2022-09-25 10:54:00 +00:00
lastWords = err . Error ( )
2022-09-04 07:44:29 +00:00
return err
2022-08-23 12:34:47 +00:00
}
2022-09-03 07:57:53 +00:00
t . log . Debugf ( "workflows path: %s" , workflowsPath )
2022-09-25 10:54:00 +00:00
workflow , err := model . ReadWorkflow ( bytes . NewReader ( task . WorkflowPayload ) )
2022-08-23 12:34:47 +00:00
if err != nil {
2022-09-25 10:54:00 +00:00
lastWords = err . Error ( )
2022-09-04 07:44:29 +00:00
return err
2022-08-23 12:34:47 +00:00
}
var plan * model . Plan
2022-10-29 04:26:27 +00:00
jobIDs := workflow . GetJobIDs ( )
if len ( jobIDs ) != 1 {
err := fmt . Errorf ( "multiple jobs found: %v" , jobIDs )
2022-09-25 10:54:00 +00:00
lastWords = err . Error ( )
return err
2022-08-23 12:34:47 +00:00
}
2022-10-29 04:26:27 +00:00
jobID := jobIDs [ 0 ]
plan = model . CombineWorkflowPlanner ( workflow ) . PlanJob ( jobID )
job := workflow . GetJob ( jobID )
reporter . ResetSteps ( len ( job . Steps ) )
2022-08-23 12:34:47 +00:00
2022-09-25 10:54:00 +00:00
log . Infof ( "plan: %+v" , plan . Stages [ 0 ] . Runs )
2022-10-31 07:31:42 +00:00
token := getToken ( task )
2022-09-25 10:54:00 +00:00
dataContext := task . Context . Fields
2022-11-04 09:23:59 +00:00
2022-11-16 10:08:12 +00:00
log . Infof ( "task %v repo is %v %v %v" , task . Id , dataContext [ "repository" ] . GetStringValue ( ) ,
2022-12-06 08:37:38 +00:00
dataContext [ "gitea_default_actions_url" ] . GetStringValue ( ) ,
2022-11-16 10:08:12 +00:00
t . client . Address ( ) )
2022-11-04 09:23:59 +00:00
2022-09-25 10:54:00 +00:00
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 07:31:42 +00:00
Token : token ,
2022-09-25 10:54:00 +00: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 07:44:29 +00:00
return err
2022-08-23 12:34:47 +00:00
}
2022-11-24 06:47:51 +00:00
maxLifetime := 3 * time . Hour
if deadline , ok := ctx . Deadline ( ) ; ok {
maxLifetime = time . Until ( deadline )
}
2022-08-23 12:34:47 +00:00
input := t . Input
config := & runner . Config {
2023-03-06 05:24:32 +00:00
Workdir : "." + string ( filepath . Separator ) ,
2022-11-18 08:12:21 +00:00
BindWorkdir : false ,
ReuseContainers : false ,
2022-09-25 10:54:00 +00:00
ForcePull : input . forcePull ,
ForceRebuild : input . forceRebuild ,
LogOutput : true ,
JSONLogger : input . jsonLogger ,
2022-11-08 10:35:13 +00:00
Env : input . envs ,
2022-09-25 10:54:00 +00:00
Secrets : task . Secrets ,
2022-08-23 12:34:47 +00:00
InsecureSecrets : input . insecureSecrets ,
Privileged : input . privileged ,
UsernsMode : input . usernsMode ,
ContainerArchitecture : input . containerArchitecture ,
ContainerDaemonSocket : input . containerDaemonSocket ,
UseGitIgnore : input . useGitIgnore ,
2022-11-16 10:08:12 +00:00
GitHubInstance : t . client . Address ( ) ,
2022-08-23 12:34:47 +00:00
ContainerCapAdd : input . containerCapAdd ,
ContainerCapDrop : input . containerCapDrop ,
2022-11-18 08:12:21 +00:00
AutoRemove : true ,
2022-08-23 12:34:47 +00:00
ArtifactServerPath : input . artifactServerPath ,
ArtifactServerPort : input . artifactServerPort ,
2022-11-18 08:12:21 +00:00
NoSkipCheckout : true ,
2022-09-25 10:54:00 +00:00
PresetGitHubContext : preset ,
EventJSON : string ( eventJSON ) ,
2022-12-06 08:37:38 +00:00
ContainerNamePrefix : fmt . Sprintf ( "GITEA-ACTIONS-TASK-%d" , task . Id ) ,
2022-11-24 06:47:51 +00:00
ContainerMaxLifetime : maxLifetime ,
2022-11-18 08:12:21 +00:00
ContainerNetworkMode : input . containerNetworkMode ,
2022-12-06 08:37:38 +00:00
DefaultActionInstance : dataContext [ "gitea_default_actions_url" ] . GetStringValue ( ) ,
2022-11-24 03:55:52 +00:00
PlatformPicker : t . platformPicker ,
2022-08-23 12:34:47 +00:00
}
r , err := runner . New ( config )
if err != nil {
2022-09-25 10:54:00 +00:00
lastWords = err . Error ( )
2022-09-04 07:44:29 +00:00
return err
2022-08-23 12:34:47 +00:00
}
2022-10-29 04:26:27 +00:00
artifactCancel := artifacts . Serve ( ctx , input . artifactServerPath , input . artifactServerPort )
2022-09-03 07:57:53 +00:00
t . log . Debugf ( "artifacts server started at %s:%s" , input . artifactServerPath , input . artifactServerPort )
2022-08-23 12:34:47 +00:00
executor := r . NewPlanExecutor ( plan ) . Finally ( func ( ctx context . Context ) error {
2022-10-29 04:26:27 +00:00
artifactCancel ( )
2022-08-23 12:34:47 +00:00
return nil
} )
2022-09-03 07:57:53 +00:00
t . log . Infof ( "workflow prepared" )
2022-09-25 10:54:00 +00:00
reporter . Logf ( "workflow prepared" )
2022-09-03 07:57:53 +00:00
// add logger recorders
2022-09-25 10:54:00 +00:00
ctx = common . WithLoggerHook ( ctx , reporter )
2022-09-03 07:57:53 +00:00
2022-08-23 12:34:47 +00:00
if err := executor ( ctx ) ; err != nil {
2022-09-25 10:54:00 +00:00
lastWords = err . Error ( )
2022-09-04 07:44:29 +00:00
return err
2022-08-23 12:34:47 +00:00
}
2022-09-03 07:57:53 +00:00
2022-09-04 07:44:29 +00:00
return nil
2022-08-23 12:34:47 +00:00
}