2020-02-05 00:38:41 +00:00
package runner
2019-01-31 07:53:39 +00:00
import (
"context"
2020-02-10 23:27:05 +00:00
"fmt"
2021-05-04 21:50:35 +00:00
"os"
2020-02-24 18:56:49 +00:00
"path/filepath"
2021-05-04 21:50:35 +00:00
"runtime"
"strings"
2019-01-31 07:53:39 +00:00
"testing"
2020-03-06 20:30:24 +00:00
"github.com/joho/godotenv"
2019-01-31 07:53:39 +00:00
log "github.com/sirupsen/logrus"
2021-05-18 06:14:49 +00:00
assert "github.com/stretchr/testify/assert"
2021-03-29 04:08:40 +00:00
2022-06-07 14:19:30 +00:00
"github.com/nektos/act/pkg/common"
2021-03-29 04:08:40 +00:00
"github.com/nektos/act/pkg/model"
2019-01-31 07:53:39 +00:00
)
2022-04-05 15:41:36 +00:00
var (
baseImage = "node:16-buster-slim"
platforms map [ string ] string
logLevel = log . DebugLevel
workdir = "testdata"
)
2021-06-10 23:12:05 +00:00
func init ( ) {
if p := os . Getenv ( "ACT_TEST_IMAGE" ) ; p != "" {
baseImage = p
}
2022-04-05 15:41:36 +00:00
platforms = map [ string ] string {
"ubuntu-latest" : baseImage ,
}
if l := os . Getenv ( "ACT_TEST_LOG_LEVEL" ) ; l != "" {
if lvl , err := log . ParseLevel ( l ) ; err == nil {
logLevel = lvl
}
}
if wd , err := filepath . Abs ( workdir ) ; err == nil {
workdir = wd
}
2021-06-10 23:12:05 +00:00
}
2019-04-08 11:01:49 +00:00
func TestGraphEvent ( t * testing . T ) {
2021-05-03 14:57:24 +00:00
planner , err := model . NewWorkflowPlanner ( "testdata/basic" , true )
2021-05-18 06:14:49 +00:00
assert . Nil ( t , err )
2019-04-08 11:01:49 +00:00
2020-02-10 23:27:05 +00:00
plan := planner . PlanEvent ( "push" )
2021-05-18 06:14:49 +00:00
assert . Nil ( t , err )
2020-02-20 03:20:47 +00:00
assert . Equal ( t , len ( plan . Stages ) , 3 , "stages" )
2020-02-10 23:27:05 +00:00
assert . Equal ( t , len ( plan . Stages [ 0 ] . Runs ) , 1 , "stage0.runs" )
assert . Equal ( t , len ( plan . Stages [ 1 ] . Runs ) , 1 , "stage1.runs" )
2020-02-20 03:20:47 +00:00
assert . Equal ( t , len ( plan . Stages [ 2 ] . Runs ) , 1 , "stage2.runs" )
assert . Equal ( t , plan . Stages [ 0 ] . Runs [ 0 ] . JobID , "check" , "jobid" )
assert . Equal ( t , plan . Stages [ 1 ] . Runs [ 0 ] . JobID , "build" , "jobid" )
assert . Equal ( t , plan . Stages [ 2 ] . Runs [ 0 ] . JobID , "test" , "jobid" )
2019-04-08 11:01:49 +00:00
2020-02-10 23:27:05 +00:00
plan = planner . PlanEvent ( "release" )
assert . Equal ( t , len ( plan . Stages ) , 0 , "stages" )
2019-04-08 11:01:49 +00:00
}
2020-11-30 05:45:11 +00:00
type TestJobFileInfo struct {
2022-04-05 15:41:36 +00:00
workdir string
workflowPath string
eventName string
errorMessage string
platforms map [ string ] string
2020-11-30 05:45:11 +00:00
}
2022-04-05 15:41:36 +00:00
func ( j * TestJobFileInfo ) runTest ( ctx context . Context , t * testing . T , cfg * Config ) {
2022-04-20 18:34:27 +00:00
fmt . Printf ( "::group::%s\n" , j . workflowPath )
2022-04-05 15:41:36 +00:00
log . SetLevel ( logLevel )
2021-05-04 21:50:35 +00:00
2022-04-05 15:41:36 +00:00
workdir , err := filepath . Abs ( j . workdir )
assert . Nil ( t , err , workdir )
2020-11-30 05:45:11 +00:00
2022-04-05 15:41:36 +00:00
fullWorkflowPath := filepath . Join ( workdir , j . workflowPath )
runnerConfig := & Config {
Workdir : workdir ,
BindWorkdir : false ,
EventName : j . eventName ,
EventPath : cfg . EventPath ,
Platforms : j . platforms ,
ReuseContainers : false ,
Env : cfg . Env ,
Secrets : cfg . Secrets ,
GitHubInstance : "github.com" ,
ContainerArchitecture : cfg . ContainerArchitecture ,
}
2020-11-30 05:45:11 +00:00
2022-04-05 15:41:36 +00:00
runner , err := New ( runnerConfig )
assert . Nil ( t , err , j . workflowPath )
2020-11-30 05:45:11 +00:00
2022-04-05 15:41:36 +00:00
planner , err := model . NewWorkflowPlanner ( fullWorkflowPath , true )
assert . Nil ( t , err , fullWorkflowPath )
plan := planner . PlanEvent ( j . eventName )
err = runner . NewPlanExecutor ( plan ) ( ctx )
if j . errorMessage == "" {
assert . Nil ( t , err , fullWorkflowPath )
} else {
assert . Error ( t , err , j . errorMessage )
}
2022-04-20 18:34:27 +00:00
fmt . Println ( "::endgroup::" )
2022-04-05 15:41:36 +00:00
}
2019-01-31 07:53:39 +00:00
func TestRunEvent ( t * testing . T ) {
2019-02-07 17:09:19 +00:00
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
2022-04-05 15:41:36 +00:00
ctx := context . Background ( )
2021-05-05 05:57:33 +00:00
2020-11-30 05:45:11 +00:00
tables := [ ] TestJobFileInfo {
2022-04-05 15:41:36 +00:00
// Shells
{ workdir , "shells/defaults" , "push" , "" , platforms } ,
2021-12-22 06:37:16 +00:00
// TODO: figure out why it fails
2022-07-08 00:21:51 +00:00
// {workdir, "shells/custom", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}, }, // custom image with pwsh
{ workdir , "shells/pwsh" , "push" , "" , map [ string ] string { "ubuntu-latest" : "catthehacker/ubuntu:pwsh-latest" } } , // custom image with pwsh
2022-04-05 15:41:36 +00:00
{ workdir , "shells/bash" , "push" , "" , platforms } ,
{ workdir , "shells/python" , "push" , "" , map [ string ] string { "ubuntu-latest" : "node:16-buster" } } , // slim doesn't have python
{ workdir , "shells/sh" , "push" , "" , platforms } ,
// Local action
{ workdir , "local-action-docker-url" , "push" , "" , platforms } ,
{ workdir , "local-action-dockerfile" , "push" , "" , platforms } ,
{ workdir , "local-action-via-composite-dockerfile" , "push" , "" , platforms } ,
{ workdir , "local-action-js" , "push" , "" , platforms } ,
// Uses
{ workdir , "uses-composite" , "push" , "" , platforms } ,
{ workdir , "uses-composite-with-error" , "push" , "Job 'failing-composite-action' failed" , platforms } ,
{ workdir , "uses-nested-composite" , "push" , "" , platforms } ,
2022-09-26 06:08:08 +00:00
{ workdir , "remote-action-composite-js-pre-with-defaults" , "push" , "" , platforms } ,
2022-04-05 15:41:36 +00:00
{ workdir , "uses-workflow" , "push" , "reusable workflows are currently not supported (see https://github.com/nektos/act/issues/826 for updates)" , platforms } ,
{ workdir , "uses-docker-url" , "push" , "" , platforms } ,
2022-05-11 19:06:05 +00:00
{ workdir , "act-composite-env-test" , "push" , "" , platforms } ,
2022-04-05 15:41:36 +00:00
// Eval
{ workdir , "evalmatrix" , "push" , "" , platforms } ,
{ workdir , "evalmatrixneeds" , "push" , "" , platforms } ,
{ workdir , "evalmatrixneeds2" , "push" , "" , platforms } ,
{ workdir , "evalmatrix-merge-map" , "push" , "" , platforms } ,
{ workdir , "evalmatrix-merge-array" , "push" , "" , platforms } ,
2022-06-08 15:25:51 +00:00
{ workdir , "issue-1195" , "push" , "" , platforms } ,
2022-04-05 15:41:36 +00:00
{ workdir , "basic" , "push" , "" , platforms } ,
{ workdir , "fail" , "push" , "exit with `FAILURE`: 1" , platforms } ,
{ workdir , "runs-on" , "push" , "" , platforms } ,
{ workdir , "checkout" , "push" , "" , platforms } ,
{ workdir , "job-container" , "push" , "" , platforms } ,
{ workdir , "job-container-non-root" , "push" , "" , platforms } ,
{ workdir , "container-hostname" , "push" , "" , platforms } ,
{ workdir , "remote-action-docker" , "push" , "" , platforms } ,
{ workdir , "remote-action-js" , "push" , "" , platforms } ,
2022-07-08 00:21:51 +00:00
{ workdir , "remote-action-js" , "push" , "" , map [ string ] string { "ubuntu-latest" : "catthehacker/ubuntu:runner-latest" } } , // Test if this works with non root container
2022-04-05 15:41:36 +00:00
{ workdir , "matrix" , "push" , "" , platforms } ,
{ workdir , "matrix-include-exclude" , "push" , "" , platforms } ,
{ workdir , "commands" , "push" , "" , platforms } ,
{ workdir , "workdir" , "push" , "" , platforms } ,
{ workdir , "defaults-run" , "push" , "" , platforms } ,
{ workdir , "composite-fail-with-output" , "push" , "" , platforms } ,
{ workdir , "issue-597" , "push" , "" , platforms } ,
{ workdir , "issue-598" , "push" , "" , platforms } ,
{ workdir , "if-env-act" , "push" , "" , platforms } ,
{ workdir , "env-and-path" , "push" , "" , platforms } ,
{ workdir , "non-existent-action" , "push" , "Job 'nopanic' failed" , platforms } ,
{ workdir , "outputs" , "push" , "" , platforms } ,
{ workdir , "steps-context/conclusion" , "push" , "" , platforms } ,
{ workdir , "steps-context/outcome" , "push" , "" , platforms } ,
{ workdir , "job-status-check" , "push" , "job 'fail' failed" , platforms } ,
{ workdir , "if-expressions" , "push" , "Job 'mytest' failed" , platforms } ,
2022-05-11 19:06:05 +00:00
{ workdir , "actions-environment-and-context-tests" , "push" , "" , platforms } ,
2022-05-24 13:36:06 +00:00
{ workdir , "uses-action-with-pre-and-post-step" , "push" , "" , platforms } ,
2022-07-27 19:46:04 +00:00
{ workdir , "evalenv" , "push" , "" , platforms } ,
2022-07-29 19:43:24 +00:00
{ workdir , "ensure-post-steps" , "push" , "Job 'second-post-step-should-fail' failed" , platforms } ,
2022-04-05 15:41:36 +00:00
{ "../model/testdata" , "strategy" , "push" , "" , platforms } , // TODO: move all testdata into pkg so we can validate it with planner and runner
// {"testdata", "issue-228", "push", "", platforms, }, // TODO [igni]: Remove this once everything passes
{ "../model/testdata" , "container-volumes" , "push" , "" , platforms } ,
}
2020-02-10 23:27:05 +00:00
2019-01-31 07:53:39 +00:00
for _ , table := range tables {
2022-04-19 19:35:42 +00:00
t . Run ( table . workflowPath , func ( t * testing . T ) {
table . runTest ( ctx , t , & Config { } )
} )
2019-01-31 07:53:39 +00:00
}
}
2020-03-06 20:30:24 +00:00
2022-06-07 14:19:30 +00:00
func TestDryrunEvent ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
ctx := common . WithDryrun ( context . Background ( ) , true )
tables := [ ] TestJobFileInfo {
// Shells
{ workdir , "shells/defaults" , "push" , "" , platforms } ,
2022-07-08 00:21:51 +00:00
{ workdir , "shells/pwsh" , "push" , "" , map [ string ] string { "ubuntu-latest" : "catthehacker/ubuntu:pwsh-latest" } } , // custom image with pwsh
2022-06-07 14:19:30 +00:00
{ workdir , "shells/bash" , "push" , "" , platforms } ,
{ workdir , "shells/python" , "push" , "" , map [ string ] string { "ubuntu-latest" : "node:16-buster" } } , // slim doesn't have python
{ workdir , "shells/sh" , "push" , "" , platforms } ,
// Local action
{ workdir , "local-action-docker-url" , "push" , "" , platforms } ,
{ workdir , "local-action-dockerfile" , "push" , "" , platforms } ,
{ workdir , "local-action-via-composite-dockerfile" , "push" , "" , platforms } ,
{ workdir , "local-action-js" , "push" , "" , platforms } ,
}
for _ , table := range tables {
t . Run ( table . workflowPath , func ( t * testing . T ) {
table . runTest ( ctx , t , & Config { } )
} )
}
}
2022-04-05 15:41:36 +00:00
func TestRunDifferentArchitecture ( t * testing . T ) {
2020-03-06 20:30:24 +00:00
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
2022-04-05 15:41:36 +00:00
tjfi := TestJobFileInfo {
workdir : workdir ,
workflowPath : "basic" ,
eventName : "push" ,
errorMessage : "" ,
platforms : platforms ,
2020-03-06 20:30:24 +00:00
}
2022-04-05 15:41:36 +00:00
tjfi . runTest ( context . Background ( ) , t , & Config { ContainerArchitecture : "linux/arm64" } )
}
2020-03-06 20:30:24 +00:00
2022-05-11 19:06:05 +00:00
func TestMaskValues ( t * testing . T ) {
assertNoSecret := func ( text string , secret string ) {
index := strings . Index ( text , "composite secret" )
if index > - 1 {
fmt . Printf ( "\nFound Secret in the given text:\n%s\n" , text )
}
assert . False ( t , strings . Contains ( text , "composite secret" ) )
}
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
log . SetLevel ( log . DebugLevel )
tjfi := TestJobFileInfo {
workdir : workdir ,
workflowPath : "mask-values" ,
eventName : "push" ,
errorMessage : "" ,
platforms : platforms ,
}
output := captureOutput ( t , func ( ) {
tjfi . runTest ( context . Background ( ) , t , & Config { } )
} )
assertNoSecret ( output , "secret value" )
2022-06-17 15:55:21 +00:00
assertNoSecret ( output , "YWJjCg==" )
2022-05-11 19:06:05 +00:00
}
2022-04-05 15:41:36 +00:00
func TestRunEventSecrets ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
2020-03-06 20:30:24 +00:00
}
2022-04-05 15:41:36 +00:00
workflowPath := "secrets"
2020-03-06 20:30:24 +00:00
2022-04-05 15:41:36 +00:00
tjfi := TestJobFileInfo {
workdir : workdir ,
workflowPath : workflowPath ,
eventName : "push" ,
errorMessage : "" ,
platforms : platforms ,
}
2020-03-06 20:30:24 +00:00
2022-04-05 15:41:36 +00:00
env , err := godotenv . Read ( filepath . Join ( workdir , workflowPath , ".env" ) )
assert . NoError ( t , err , "Failed to read .env" )
secrets , _ := godotenv . Read ( filepath . Join ( workdir , workflowPath , ".secrets" ) )
assert . NoError ( t , err , "Failed to read .secrets" )
2020-03-06 20:30:24 +00:00
2022-04-05 15:41:36 +00:00
tjfi . runTest ( context . Background ( ) , t , & Config { Secrets : secrets , Env : env } )
2020-03-06 20:30:24 +00:00
}
2020-03-06 22:17:57 +00:00
func TestRunEventPullRequest ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
workflowPath := "pull-request"
2022-04-05 15:41:36 +00:00
tjfi := TestJobFileInfo {
workdir : workdir ,
workflowPath : workflowPath ,
eventName : "pull_request" ,
errorMessage : "" ,
platforms : platforms ,
2020-03-06 22:17:57 +00:00
}
2022-04-05 15:41:36 +00:00
tjfi . runTest ( context . Background ( ) , t , & Config { EventPath : filepath . Join ( workdir , workflowPath , "event.json" ) } )
2020-03-06 22:17:57 +00:00
}
2021-05-04 21:50:35 +00:00
func TestContainerPath ( t * testing . T ) {
type containerPathJob struct {
destinationPath string
sourcePath string
workDir string
}
if runtime . GOOS == "windows" {
cwd , err := os . Getwd ( )
if err != nil {
log . Error ( err )
}
rootDrive := os . Getenv ( "SystemDrive" )
rootDriveLetter := strings . ReplaceAll ( strings . ToLower ( rootDrive ) , ` : ` , "" )
for _ , v := range [ ] containerPathJob {
{ "/mnt/c/Users/act/go/src/github.com/nektos/act" , "C:\\Users\\act\\go\\src\\github.com\\nektos\\act\\" , "" } ,
{ "/mnt/f/work/dir" , ` F:\work\dir ` , "" } ,
2021-05-10 15:12:57 +00:00
{ "/mnt/c/windows/to/unix" , "windows\\to\\unix" , fmt . Sprintf ( "%s\\" , rootDrive ) } ,
2021-05-04 21:50:35 +00:00
{ fmt . Sprintf ( "/mnt/%v/act" , rootDriveLetter ) , "act" , fmt . Sprintf ( "%s\\" , rootDrive ) } ,
} {
if v . workDir != "" {
if err := os . Chdir ( v . workDir ) ; err != nil {
log . Error ( err )
t . Fail ( )
}
}
runnerConfig := & Config {
Workdir : v . sourcePath ,
}
assert . Equal ( t , v . destinationPath , runnerConfig . containerPath ( runnerConfig . Workdir ) )
}
if err := os . Chdir ( cwd ) ; err != nil {
log . Error ( err )
}
} else {
cwd , err := os . Getwd ( )
if err != nil {
log . Error ( err )
}
for _ , v := range [ ] containerPathJob {
{ "/home/act/go/src/github.com/nektos/act" , "/home/act/go/src/github.com/nektos/act" , "" } ,
{ "/home/act" , ` /home/act/ ` , "" } ,
{ cwd , "." , "" } ,
} {
runnerConfig := & Config {
Workdir : v . sourcePath ,
}
assert . Equal ( t , v . destinationPath , runnerConfig . containerPath ( runnerConfig . Workdir ) )
}
}
}