2020-02-04 18:38:41 -06:00
package runner
2019-01-31 01:53:39 -06:00
import (
2022-12-09 04:25:32 -06:00
"bytes"
2019-01-31 01:53:39 -06:00
"context"
2020-02-10 17:27:05 -06:00
"fmt"
2022-12-09 04:25:32 -06:00
"io"
2021-05-04 16:50:35 -05:00
"os"
2020-02-24 12:56:49 -06:00
"path/filepath"
2021-05-04 16:50:35 -05:00
"runtime"
"strings"
2019-01-31 01:53:39 -06:00
"testing"
2020-03-06 14:30:24 -06:00
"github.com/joho/godotenv"
2019-01-31 01:53:39 -06:00
log "github.com/sirupsen/logrus"
2021-05-18 01:14:49 -05:00
assert "github.com/stretchr/testify/assert"
2021-03-28 23:08:40 -05:00
2022-06-07 09:19:30 -05:00
"github.com/nektos/act/pkg/common"
2021-03-28 23:08:40 -05:00
"github.com/nektos/act/pkg/model"
2019-01-31 01:53:39 -06:00
)
2022-04-05 10:41:36 -05:00
var (
baseImage = "node:16-buster-slim"
platforms map [ string ] string
logLevel = log . DebugLevel
workdir = "testdata"
2022-12-15 10:45:22 -06:00
secrets map [ string ] string
2022-04-05 10:41:36 -05:00
)
2021-06-10 18:12:05 -05:00
func init ( ) {
if p := os . Getenv ( "ACT_TEST_IMAGE" ) ; p != "" {
baseImage = p
}
2022-04-05 10:41:36 -05: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
}
2022-12-15 10:45:22 -06:00
secrets = map [ string ] string { }
2021-06-10 18:12:05 -05:00
}
2023-02-16 10:41:59 -06:00
func TestNoWorkflowsFoundByPlanner ( t * testing . T ) {
planner , err := model . NewWorkflowPlanner ( "res" , true )
assert . NoError ( t , err )
out := log . StandardLogger ( ) . Out
var buf bytes . Buffer
log . SetOutput ( & buf )
log . SetLevel ( log . DebugLevel )
plan , err := planner . PlanEvent ( "pull_request" )
assert . NotNil ( t , plan )
assert . NoError ( t , err )
assert . Contains ( t , buf . String ( ) , "no workflows found by planner" )
buf . Reset ( )
plan , err = planner . PlanAll ( )
assert . NotNil ( t , plan )
assert . NoError ( t , err )
assert . Contains ( t , buf . String ( ) , "no workflows found by planner" )
log . SetOutput ( out )
}
func TestGraphMissingEvent ( t * testing . T ) {
planner , err := model . NewWorkflowPlanner ( "testdata/issue-1595/no-event.yml" , true )
assert . NoError ( t , err )
out := log . StandardLogger ( ) . Out
var buf bytes . Buffer
log . SetOutput ( & buf )
log . SetLevel ( log . DebugLevel )
plan , err := planner . PlanEvent ( "push" )
assert . NoError ( t , err )
assert . NotNil ( t , plan )
assert . Equal ( t , 0 , len ( plan . Stages ) )
assert . Contains ( t , buf . String ( ) , "no events found for workflow: no-event.yml" )
log . SetOutput ( out )
}
func TestGraphMissingFirst ( t * testing . T ) {
planner , err := model . NewWorkflowPlanner ( "testdata/issue-1595/no-first.yml" , true )
assert . NoError ( t , err )
plan , err := planner . PlanEvent ( "push" )
assert . EqualError ( t , err , "unable to build dependency graph for no first (no-first.yml)" )
assert . NotNil ( t , plan )
assert . Equal ( t , 0 , len ( plan . Stages ) )
}
func TestGraphWithMissing ( t * testing . T ) {
planner , err := model . NewWorkflowPlanner ( "testdata/issue-1595/missing.yml" , true )
assert . NoError ( t , err )
out := log . StandardLogger ( ) . Out
var buf bytes . Buffer
log . SetOutput ( & buf )
log . SetLevel ( log . DebugLevel )
plan , err := planner . PlanEvent ( "push" )
assert . NotNil ( t , plan )
assert . Equal ( t , 0 , len ( plan . Stages ) )
assert . EqualError ( t , err , "unable to build dependency graph for missing (missing.yml)" )
assert . Contains ( t , buf . String ( ) , "unable to build dependency graph for missing (missing.yml)" )
log . SetOutput ( out )
}
func TestGraphWithSomeMissing ( t * testing . T ) {
log . SetLevel ( log . DebugLevel )
planner , err := model . NewWorkflowPlanner ( "testdata/issue-1595/" , true )
assert . NoError ( t , err )
out := log . StandardLogger ( ) . Out
var buf bytes . Buffer
log . SetOutput ( & buf )
log . SetLevel ( log . DebugLevel )
plan , err := planner . PlanAll ( )
assert . Error ( t , err , "unable to build dependency graph for no first (no-first.yml)" )
assert . NotNil ( t , plan )
assert . Equal ( t , 1 , len ( plan . Stages ) )
assert . Contains ( t , buf . String ( ) , "unable to build dependency graph for missing (missing.yml)" )
assert . Contains ( t , buf . String ( ) , "unable to build dependency graph for no first (no-first.yml)" )
log . SetOutput ( out )
}
2019-04-08 06:01:49 -05:00
func TestGraphEvent ( t * testing . T ) {
2021-05-03 09:57:24 -05:00
planner , err := model . NewWorkflowPlanner ( "testdata/basic" , true )
2023-02-16 10:41:59 -06:00
assert . NoError ( t , err )
2019-04-08 06:01:49 -05:00
2023-02-16 10:41:59 -06:00
plan , err := planner . PlanEvent ( "push" )
assert . NoError ( t , err )
assert . NotNil ( t , plan )
assert . NotNil ( t , plan . Stages )
2020-02-19 21:20:47 -06:00
assert . Equal ( t , len ( plan . Stages ) , 3 , "stages" )
2020-02-10 17:27:05 -06: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-19 21:20:47 -06: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 06:01:49 -05:00
2023-02-16 10:41:59 -06:00
plan , err = planner . PlanEvent ( "release" )
assert . NoError ( t , err )
assert . NotNil ( t , plan )
assert . Equal ( t , 0 , len ( plan . Stages ) )
2019-04-08 06:01:49 -05:00
}
2020-11-29 23:45:11 -06:00
type TestJobFileInfo struct {
2022-04-05 10:41:36 -05:00
workdir string
workflowPath string
eventName string
errorMessage string
platforms map [ string ] string
2022-12-15 10:45:22 -06:00
secrets map [ string ] string
2020-11-29 23:45:11 -06:00
}
2022-04-05 10:41:36 -05:00
func ( j * TestJobFileInfo ) runTest ( ctx context . Context , t * testing . T , cfg * Config ) {
2022-04-20 13:34:27 -05:00
fmt . Printf ( "::group::%s\n" , j . workflowPath )
2022-04-05 10:41:36 -05:00
log . SetLevel ( logLevel )
2021-05-04 16:50:35 -05:00
2022-04-05 10:41:36 -05:00
workdir , err := filepath . Abs ( j . workdir )
assert . Nil ( t , err , workdir )
2020-11-29 23:45:11 -06:00
2022-04-05 10:41:36 -05: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 ,
2023-01-13 13:28:17 -06:00
Inputs : cfg . Inputs ,
2022-04-05 10:41:36 -05:00
GitHubInstance : "github.com" ,
ContainerArchitecture : cfg . ContainerArchitecture ,
2023-03-19 12:25:55 -05:00
Matrix : cfg . Matrix ,
2022-04-05 10:41:36 -05:00
}
2020-11-29 23:45:11 -06:00
2022-04-05 10:41:36 -05:00
runner , err := New ( runnerConfig )
assert . Nil ( t , err , j . workflowPath )
2020-11-29 23:45:11 -06:00
2022-04-05 10:41:36 -05:00
planner , err := model . NewWorkflowPlanner ( fullWorkflowPath , true )
assert . Nil ( t , err , fullWorkflowPath )
2023-02-16 10:41:59 -06:00
plan , err := planner . PlanEvent ( j . eventName )
assert . True ( t , ( err == nil ) != ( plan == nil ) , "PlanEvent should return either a plan or an error" )
if err == nil && plan != nil {
err = runner . NewPlanExecutor ( plan ) ( ctx )
if j . errorMessage == "" {
assert . Nil ( t , err , fullWorkflowPath )
} else {
assert . Error ( t , err , j . errorMessage )
}
2022-04-05 10:41:36 -05:00
}
2022-04-20 13:34:27 -05:00
fmt . Println ( "::endgroup::" )
2022-04-05 10:41:36 -05:00
}
2019-01-31 01:53:39 -06:00
func TestRunEvent ( t * testing . T ) {
2019-02-07 11:09:19 -06:00
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
2022-04-05 10:41:36 -05:00
ctx := context . Background ( )
2021-05-05 00:57:33 -05:00
2020-11-29 23:45:11 -06:00
tables := [ ] TestJobFileInfo {
2022-04-05 10:41:36 -05:00
// Shells
2022-12-15 10:45:22 -06:00
{ workdir , "shells/defaults" , "push" , "" , platforms , secrets } ,
2021-12-22 00:37:16 -06:00
// TODO: figure out why it fails
2022-07-07 19:21:51 -05:00
// {workdir, "shells/custom", "push", "", map[string]string{"ubuntu-latest": "catthehacker/ubuntu:pwsh-latest"}, }, // custom image with pwsh
2022-12-15 10:45:22 -06:00
{ workdir , "shells/pwsh" , "push" , "" , map [ string ] string { "ubuntu-latest" : "catthehacker/ubuntu:pwsh-latest" } , secrets } , // custom image with pwsh
{ workdir , "shells/bash" , "push" , "" , platforms , secrets } ,
{ workdir , "shells/python" , "push" , "" , map [ string ] string { "ubuntu-latest" : "node:16-buster" } , secrets } , // slim doesn't have python
{ workdir , "shells/sh" , "push" , "" , platforms , secrets } ,
2022-04-05 10:41:36 -05:00
// Local action
2022-12-15 10:45:22 -06:00
{ workdir , "local-action-docker-url" , "push" , "" , platforms , secrets } ,
{ workdir , "local-action-dockerfile" , "push" , "" , platforms , secrets } ,
{ workdir , "local-action-via-composite-dockerfile" , "push" , "" , platforms , secrets } ,
{ workdir , "local-action-js" , "push" , "" , platforms , secrets } ,
2022-04-05 10:41:36 -05:00
// Uses
2022-12-15 10:45:22 -06:00
{ workdir , "uses-composite" , "push" , "" , platforms , secrets } ,
{ workdir , "uses-composite-with-error" , "push" , "Job 'failing-composite-action' failed" , platforms , secrets } ,
{ workdir , "uses-nested-composite" , "push" , "" , platforms , secrets } ,
{ workdir , "remote-action-composite-js-pre-with-defaults" , "push" , "" , platforms , secrets } ,
2023-10-03 18:13:05 -05:00
{ workdir , "remote-action-composite-action-ref" , "push" , "" , platforms , secrets } ,
2023-01-19 14:49:11 -06:00
{ workdir , "uses-workflow" , "push" , "" , platforms , map [ string ] string { "secret" : "keep_it_private" } } ,
2022-12-15 10:45:22 -06:00
{ workdir , "uses-workflow" , "pull_request" , "" , platforms , map [ string ] string { "secret" : "keep_it_private" } } ,
{ workdir , "uses-docker-url" , "push" , "" , platforms , secrets } ,
{ workdir , "act-composite-env-test" , "push" , "" , platforms , secrets } ,
2022-04-05 10:41:36 -05:00
// Eval
2022-12-15 10:45:22 -06:00
{ workdir , "evalmatrix" , "push" , "" , platforms , secrets } ,
{ workdir , "evalmatrixneeds" , "push" , "" , platforms , secrets } ,
{ workdir , "evalmatrixneeds2" , "push" , "" , platforms , secrets } ,
{ workdir , "evalmatrix-merge-map" , "push" , "" , platforms , secrets } ,
{ workdir , "evalmatrix-merge-array" , "push" , "" , platforms , secrets } ,
{ workdir , "issue-1195" , "push" , "" , platforms , secrets } ,
{ workdir , "basic" , "push" , "" , platforms , secrets } ,
{ workdir , "fail" , "push" , "exit with `FAILURE`: 1" , platforms , secrets } ,
{ workdir , "runs-on" , "push" , "" , platforms , secrets } ,
{ workdir , "checkout" , "push" , "" , platforms , secrets } ,
{ workdir , "job-container" , "push" , "" , platforms , secrets } ,
{ workdir , "job-container-non-root" , "push" , "" , platforms , secrets } ,
{ workdir , "job-container-invalid-credentials" , "push" , "failed to handle credentials: failed to interpolate container.credentials.password" , platforms , secrets } ,
{ workdir , "container-hostname" , "push" , "" , platforms , secrets } ,
{ workdir , "remote-action-docker" , "push" , "" , platforms , secrets } ,
{ workdir , "remote-action-js" , "push" , "" , platforms , secrets } ,
2023-02-04 07:35:13 -06:00
{ workdir , "remote-action-js-node-user" , "push" , "" , platforms , secrets } , // Test if this works with non root container
2022-12-15 10:45:22 -06:00
{ workdir , "matrix" , "push" , "" , platforms , secrets } ,
{ workdir , "matrix-include-exclude" , "push" , "" , platforms , secrets } ,
2023-01-10 15:31:12 -06:00
{ workdir , "matrix-exitcode" , "push" , "Job 'test' failed" , platforms , secrets } ,
2022-12-15 10:45:22 -06:00
{ workdir , "commands" , "push" , "" , platforms , secrets } ,
{ workdir , "workdir" , "push" , "" , platforms , secrets } ,
{ workdir , "defaults-run" , "push" , "" , platforms , secrets } ,
{ workdir , "composite-fail-with-output" , "push" , "" , platforms , secrets } ,
{ workdir , "issue-597" , "push" , "" , platforms , secrets } ,
{ workdir , "issue-598" , "push" , "" , platforms , secrets } ,
{ workdir , "if-env-act" , "push" , "" , platforms , secrets } ,
{ workdir , "env-and-path" , "push" , "" , platforms , secrets } ,
{ workdir , "environment-files" , "push" , "" , platforms , secrets } ,
{ workdir , "GITHUB_STATE" , "push" , "" , platforms , secrets } ,
{ workdir , "environment-files-parser-bug" , "push" , "" , platforms , secrets } ,
{ workdir , "non-existent-action" , "push" , "Job 'nopanic' failed" , platforms , secrets } ,
{ workdir , "outputs" , "push" , "" , platforms , secrets } ,
{ workdir , "networking" , "push" , "" , platforms , secrets } ,
{ workdir , "steps-context/conclusion" , "push" , "" , platforms , secrets } ,
{ workdir , "steps-context/outcome" , "push" , "" , platforms , secrets } ,
{ workdir , "job-status-check" , "push" , "job 'fail' failed" , platforms , secrets } ,
{ workdir , "if-expressions" , "push" , "Job 'mytest' failed" , platforms , secrets } ,
{ workdir , "actions-environment-and-context-tests" , "push" , "" , platforms , secrets } ,
{ workdir , "uses-action-with-pre-and-post-step" , "push" , "" , platforms , secrets } ,
{ workdir , "evalenv" , "push" , "" , platforms , secrets } ,
2023-02-04 07:35:13 -06:00
{ workdir , "docker-action-custom-path" , "push" , "" , platforms , secrets } ,
{ workdir , "GITHUB_ENV-use-in-env-ctx" , "push" , "" , platforms , secrets } ,
2022-12-15 10:45:22 -06:00
{ workdir , "ensure-post-steps" , "push" , "Job 'second-post-step-should-fail' failed" , platforms , secrets } ,
2023-06-27 12:32:04 -05:00
{ workdir , "workflow_call_inputs" , "workflow_call" , "" , platforms , secrets } ,
2022-12-15 10:45:22 -06:00
{ workdir , "workflow_dispatch" , "workflow_dispatch" , "" , platforms , secrets } ,
{ workdir , "workflow_dispatch_no_inputs_mapping" , "workflow_dispatch" , "" , platforms , secrets } ,
{ workdir , "workflow_dispatch-scalar" , "workflow_dispatch" , "" , platforms , secrets } ,
{ workdir , "workflow_dispatch-scalar-composite-action" , "workflow_dispatch" , "" , platforms , secrets } ,
2022-12-19 02:37:53 -06:00
{ workdir , "job-needs-context-contains-result" , "push" , "" , platforms , secrets } ,
2022-12-15 10:45:22 -06:00
{ "../model/testdata" , "strategy" , "push" , "" , platforms , secrets } , // TODO: move all testdata into pkg so we can validate it with planner and runner
{ "../model/testdata" , "container-volumes" , "push" , "" , platforms , secrets } ,
2023-01-10 15:55:05 -06:00
{ workdir , "path-handling" , "push" , "" , platforms , secrets } ,
2023-01-29 08:47:56 -06:00
{ workdir , "do-not-leak-step-env-in-composite" , "push" , "" , platforms , secrets } ,
2023-02-04 07:35:13 -06:00
{ workdir , "set-env-step-env-override" , "push" , "" , platforms , secrets } ,
{ workdir , "set-env-new-env-file-per-step" , "push" , "" , platforms , secrets } ,
2023-02-23 16:16:07 -06:00
{ workdir , "no-panic-on-invalid-composite-action" , "push" , "jobs failed due to invalid action" , platforms , secrets } ,
Add support for service containers (#1949)
* Support services (#42)
Removed createSimpleContainerName and AutoRemove flag
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/42
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services options (#45)
Reviewed-on: https://gitea.com/gitea/act/pulls/45
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support intepolation for `env` of `services` (#47)
Reviewed-on: https://gitea.com/gitea/act/pulls/47
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Support services `credentials` (#51)
If a service's image is from a container registry requires authentication, `act_runner` will need `credentials` to pull the image, see [documentation](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idcredentials).
Currently, `act_runner` incorrectly uses the `credentials` of `containers` to pull services' images and the `credentials` of services won't be used, see the related code: https://gitea.com/gitea/act/src/commit/0c1f2edb996a87ee17dcf3cfa7259c04be02abd7/pkg/runner/run_context.go#L228-L269
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/51
Reviewed-by: Jason Song <i@wolfogre.com>
Reviewed-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Add ContainerMaxLifetime and ContainerNetworkMode options
from: https://gitea.com/gitea/act/commit/b9c20dcaa43899cb3bb327619d447248303170e0
* Fix container network issue (#56)
Follow: https://gitea.com/gitea/act_runner/pulls/184
Close https://gitea.com/gitea/act_runner/issues/177
- `act` create new networks only if the value of `NeedCreateNetwork` is true, and remove these networks at last. `NeedCreateNetwork` is passed by `act_runner`. 'NeedCreateNetwork' is true only if `container.network` in the configuration file of the `act_runner` is empty.
- In the `docker create` phase, specify the network to which containers will connect. Because, if not specify , container will connect to `bridge` network which is created automatically by Docker.
- If the network is user defined network ( the value of `container.network` is empty or `<custom-network>`. Because, the network created by `act` is also user defined network.), will also specify alias by `--network-alias`. The alias of service is `<service-id>`. So we can be access service container by `<service-id>:<port>` in the steps of job.
- Won't try to `docker network connect ` network after `docker start` any more.
- Because on the one hand, `docker network connect` applies only to user defined networks, if try to `docker network connect host <container-name>` will return error.
- On the other hand, we just specify network in the stage of `docker create`, the same effect can be achieved.
- Won't try to remove containers and networks berfore the stage of `docker start`, because the name of these containers and netwoks won't be repeat.
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/56
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-committed-by: sillyguodong <gedong_1994@163.com>
* Check volumes (#60)
This PR adds a `ValidVolumes` config. Users can specify the volumes (including bind mounts) that can be mounted to containers by this config.
Options related to volumes:
- [jobs.<job_id>.container.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idcontainervolumes)
- [jobs.<job_id>.services.<service_id>.volumes](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idservicesservice_idvolumes)
In addition, volumes specified by `options` will also be checked.
Currently, the following default volumes (see https://gitea.com/gitea/act/src/commit/a72822b3f83d3e68ffc697101b713b7badf57e2f/pkg/runner/run_context.go#L116-L166) will be added to `ValidVolumes`:
- `act-toolcache`
- `<container-name>` and `<container-name>-env`
- `/var/run/docker.sock` (We need to add a new configuration to control whether the docker daemon can be mounted)
Co-authored-by: Jason Song <i@wolfogre.com>
Reviewed-on: https://gitea.com/gitea/act/pulls/60
Reviewed-by: Jason Song <i@wolfogre.com>
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Remove ContainerMaxLifetime; fix lint
* Remove unused ValidVolumes
* Remove ConnectToNetwork
* Add docker stubs
* Close docker clients to prevent file descriptor leaks
* Fix the error when removing network in self-hosted mode (#69)
Fixes https://gitea.com/gitea/act_runner/issues/255
Reviewed-on: https://gitea.com/gitea/act/pulls/69
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-committed-by: Zettat123 <zettat123@gmail.com>
* Move service container and network cleanup to rc.cleanUpJobContainer
* Add --network flag; default to host if not using service containers or set explicitly
* Correctly close executor to prevent fd leak
* Revert to tail instead of full path
* fix network duplication
* backport networkingConfig for aliaes
* don't hardcode netMode host
* Convert services test to table driven tests
* Add failing tests for services
* Expose service container ports onto the host
* Set container network mode in artifacts server test to host mode
* Log container network mode when creating/starting a container
* fix: Correctly handle ContainerNetworkMode
* fix: missing service container network
* Always remove service containers
Although we usually keep containers running if the workflow errored
(unless `--rm` is given) in order to facilitate debugging and we have
a flag (`--reuse`) to always keep containers running in order to speed
up repeated `act` invocations, I believe that these should only apply
to job containers and not service containers, because changing the
network settings on a service container requires re-creating it anyway.
* Remove networks only if no active endpoints exist
* Ensure job containers are stopped before starting a new job
* fix: go build -tags WITHOUT_DOCKER
---------
Co-authored-by: Zettat123 <zettat123@gmail.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: Jason Song <i@wolfogre.com>
Co-authored-by: sillyguodong <gedong_1994@163.com>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: ZauberNerd <zaubernerd@zaubernerd.de>
2023-10-19 04:24:52 -05:00
// services
{ workdir , "services" , "push" , "" , platforms , secrets } ,
{ workdir , "services-host-network" , "push" , "" , platforms , secrets } ,
{ workdir , "services-with-container" , "push" , "" , platforms , secrets } ,
2022-04-05 10:41:36 -05:00
}
2020-02-10 17:27:05 -06:00
2019-01-31 01:53:39 -06:00
for _ , table := range tables {
2022-04-19 14:35:42 -05:00
t . Run ( table . workflowPath , func ( t * testing . T ) {
2022-12-15 10:45:22 -06:00
config := & Config {
Secrets : table . secrets ,
}
2022-10-17 11:25:26 -05:00
eventFile := filepath . Join ( workdir , table . workflowPath , "event.json" )
if _ , err := os . Stat ( eventFile ) ; err == nil {
config . EventPath = eventFile
}
table . runTest ( ctx , t , config )
2022-04-19 14:35:42 -05:00
} )
2019-01-31 01:53:39 -06:00
}
}
2020-03-06 14:30:24 -06:00
2022-11-16 15:29:45 -06:00
func TestRunEventHostEnvironment ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
ctx := context . Background ( )
tables := [ ] TestJobFileInfo { }
if runtime . GOOS == "linux" {
platforms := map [ string ] string {
"ubuntu-latest" : "-self-hosted" ,
}
tables = append ( tables , [ ] TestJobFileInfo {
// Shells
2022-12-15 10:45:22 -06:00
{ workdir , "shells/defaults" , "push" , "" , platforms , secrets } ,
{ workdir , "shells/pwsh" , "push" , "" , platforms , secrets } ,
{ workdir , "shells/bash" , "push" , "" , platforms , secrets } ,
{ workdir , "shells/python" , "push" , "" , platforms , secrets } ,
{ workdir , "shells/sh" , "push" , "" , platforms , secrets } ,
2022-11-16 15:29:45 -06:00
// Local action
2022-12-15 10:45:22 -06:00
{ workdir , "local-action-js" , "push" , "" , platforms , secrets } ,
2022-11-16 15:29:45 -06:00
// Uses
2022-12-15 10:45:22 -06:00
{ workdir , "uses-composite" , "push" , "" , platforms , secrets } ,
{ workdir , "uses-composite-with-error" , "push" , "Job 'failing-composite-action' failed" , platforms , secrets } ,
{ workdir , "uses-nested-composite" , "push" , "" , platforms , secrets } ,
{ workdir , "act-composite-env-test" , "push" , "" , platforms , secrets } ,
2022-11-16 15:29:45 -06:00
// Eval
2022-12-15 10:45:22 -06:00
{ workdir , "evalmatrix" , "push" , "" , platforms , secrets } ,
{ workdir , "evalmatrixneeds" , "push" , "" , platforms , secrets } ,
{ workdir , "evalmatrixneeds2" , "push" , "" , platforms , secrets } ,
{ workdir , "evalmatrix-merge-map" , "push" , "" , platforms , secrets } ,
{ workdir , "evalmatrix-merge-array" , "push" , "" , platforms , secrets } ,
{ workdir , "issue-1195" , "push" , "" , platforms , secrets } ,
{ workdir , "fail" , "push" , "exit with `FAILURE`: 1" , platforms , secrets } ,
{ workdir , "runs-on" , "push" , "" , platforms , secrets } ,
{ workdir , "checkout" , "push" , "" , platforms , secrets } ,
{ workdir , "remote-action-js" , "push" , "" , platforms , secrets } ,
{ workdir , "matrix" , "push" , "" , platforms , secrets } ,
{ workdir , "matrix-include-exclude" , "push" , "" , platforms , secrets } ,
{ workdir , "commands" , "push" , "" , platforms , secrets } ,
{ workdir , "defaults-run" , "push" , "" , platforms , secrets } ,
{ workdir , "composite-fail-with-output" , "push" , "" , platforms , secrets } ,
{ workdir , "issue-597" , "push" , "" , platforms , secrets } ,
{ workdir , "issue-598" , "push" , "" , platforms , secrets } ,
{ workdir , "if-env-act" , "push" , "" , platforms , secrets } ,
{ workdir , "env-and-path" , "push" , "" , platforms , secrets } ,
{ workdir , "non-existent-action" , "push" , "Job 'nopanic' failed" , platforms , secrets } ,
{ workdir , "outputs" , "push" , "" , platforms , secrets } ,
{ workdir , "steps-context/conclusion" , "push" , "" , platforms , secrets } ,
{ workdir , "steps-context/outcome" , "push" , "" , platforms , secrets } ,
{ workdir , "job-status-check" , "push" , "job 'fail' failed" , platforms , secrets } ,
{ workdir , "if-expressions" , "push" , "Job 'mytest' failed" , platforms , secrets } ,
{ workdir , "uses-action-with-pre-and-post-step" , "push" , "" , platforms , secrets } ,
{ workdir , "evalenv" , "push" , "" , platforms , secrets } ,
{ workdir , "ensure-post-steps" , "push" , "Job 'second-post-step-should-fail' failed" , platforms , secrets } ,
2022-11-16 15:29:45 -06:00
} ... )
}
if runtime . GOOS == "windows" {
platforms := map [ string ] string {
"windows-latest" : "-self-hosted" ,
}
tables = append ( tables , [ ] TestJobFileInfo {
2022-12-15 10:45:22 -06:00
{ workdir , "windows-prepend-path" , "push" , "" , platforms , secrets } ,
{ workdir , "windows-add-env" , "push" , "" , platforms , secrets } ,
2023-08-08 10:44:25 -05:00
{ workdir , "windows-shell-cmd" , "push" , "" , platforms , secrets } ,
2022-11-16 15:29:45 -06:00
} ... )
} else {
platforms := map [ string ] string {
2023-01-29 08:47:56 -06:00
"self-hosted" : "-self-hosted" ,
"ubuntu-latest" : "-self-hosted" ,
2022-11-16 15:29:45 -06:00
}
tables = append ( tables , [ ] TestJobFileInfo {
2022-12-15 10:45:22 -06:00
{ workdir , "nix-prepend-path" , "push" , "" , platforms , secrets } ,
{ workdir , "inputs-via-env-context" , "push" , "" , platforms , secrets } ,
2023-01-29 08:47:56 -06:00
{ workdir , "do-not-leak-step-env-in-composite" , "push" , "" , platforms , secrets } ,
2023-02-04 07:35:13 -06:00
{ workdir , "set-env-step-env-override" , "push" , "" , platforms , secrets } ,
{ workdir , "set-env-new-env-file-per-step" , "push" , "" , platforms , secrets } ,
2023-02-23 16:16:07 -06:00
{ workdir , "no-panic-on-invalid-composite-action" , "push" , "jobs failed due to invalid action" , platforms , secrets } ,
2022-11-16 15:29:45 -06:00
} ... )
}
for _ , table := range tables {
t . Run ( table . workflowPath , func ( t * testing . T ) {
table . runTest ( ctx , t , & Config { } )
} )
}
}
2022-06-07 09:19:30 -05:00
func TestDryrunEvent ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
ctx := common . WithDryrun ( context . Background ( ) , true )
tables := [ ] TestJobFileInfo {
// Shells
2022-12-15 10:45:22 -06:00
{ workdir , "shells/defaults" , "push" , "" , platforms , secrets } ,
{ workdir , "shells/pwsh" , "push" , "" , map [ string ] string { "ubuntu-latest" : "catthehacker/ubuntu:pwsh-latest" } , secrets } , // custom image with pwsh
{ workdir , "shells/bash" , "push" , "" , platforms , secrets } ,
{ workdir , "shells/python" , "push" , "" , map [ string ] string { "ubuntu-latest" : "node:16-buster" } , secrets } , // slim doesn't have python
{ workdir , "shells/sh" , "push" , "" , platforms , secrets } ,
2022-06-07 09:19:30 -05:00
// Local action
2022-12-15 10:45:22 -06:00
{ workdir , "local-action-docker-url" , "push" , "" , platforms , secrets } ,
{ workdir , "local-action-dockerfile" , "push" , "" , platforms , secrets } ,
{ workdir , "local-action-via-composite-dockerfile" , "push" , "" , platforms , secrets } ,
{ workdir , "local-action-js" , "push" , "" , platforms , secrets } ,
2022-06-07 09:19:30 -05:00
}
for _ , table := range tables {
t . Run ( table . workflowPath , func ( t * testing . T ) {
table . runTest ( ctx , t , & Config { } )
} )
}
}
2023-03-08 08:57:49 -06:00
func TestDockerActionForcePullForceRebuild ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
ctx := context . Background ( )
config := & Config {
ForcePull : true ,
ForceRebuild : true ,
}
tables := [ ] TestJobFileInfo {
{ workdir , "local-action-dockerfile" , "push" , "" , platforms , secrets } ,
{ workdir , "local-action-via-composite-dockerfile" , "push" , "" , platforms , secrets } ,
}
for _ , table := range tables {
t . Run ( table . workflowPath , func ( t * testing . T ) {
table . runTest ( ctx , t , config )
} )
}
}
2022-04-05 10:41:36 -05:00
func TestRunDifferentArchitecture ( t * testing . T ) {
2020-03-06 14:30:24 -06:00
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
2022-04-05 10:41:36 -05:00
tjfi := TestJobFileInfo {
workdir : workdir ,
workflowPath : "basic" ,
eventName : "push" ,
errorMessage : "" ,
platforms : platforms ,
2020-03-06 14:30:24 -06:00
}
2022-04-05 10:41:36 -05:00
tjfi . runTest ( context . Background ( ) , t , & Config { ContainerArchitecture : "linux/arm64" } )
}
2020-03-06 14:30:24 -06:00
2022-12-09 04:25:32 -06:00
type maskJobLoggerFactory struct {
Output bytes . Buffer
}
func ( f * maskJobLoggerFactory ) WithJobLogger ( ) * log . Logger {
logger := log . New ( )
logger . SetOutput ( io . MultiWriter ( & f . Output , os . Stdout ) )
logger . SetLevel ( log . DebugLevel )
return logger
}
2022-05-11 14:06:05 -05: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 ,
}
2022-12-09 04:25:32 -06:00
logger := & maskJobLoggerFactory { }
tjfi . runTest ( WithJobLoggerFactory ( common . WithLogger ( context . Background ( ) , logger . WithJobLogger ( ) ) , logger ) , t , & Config { } )
output := logger . Output . String ( )
2022-05-11 14:06:05 -05:00
assertNoSecret ( output , "secret value" )
2022-06-17 10:55:21 -05:00
assertNoSecret ( output , "YWJjCg==" )
2022-05-11 14:06:05 -05:00
}
2022-04-05 10:41:36 -05:00
func TestRunEventSecrets ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
2020-03-06 14:30:24 -06:00
}
2022-04-05 10:41:36 -05:00
workflowPath := "secrets"
2020-03-06 14:30:24 -06:00
2022-04-05 10:41:36 -05:00
tjfi := TestJobFileInfo {
workdir : workdir ,
workflowPath : workflowPath ,
eventName : "push" ,
errorMessage : "" ,
platforms : platforms ,
}
2020-03-06 14:30:24 -06:00
2022-04-05 10:41:36 -05: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 14:30:24 -06:00
2022-04-05 10:41:36 -05:00
tjfi . runTest ( context . Background ( ) , t , & Config { Secrets : secrets , Env : env } )
2020-03-06 14:30:24 -06:00
}
2020-03-06 16:17:57 -06:00
2023-01-13 13:28:17 -06:00
func TestRunActionInputs ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
workflowPath := "input-from-cli"
tjfi := TestJobFileInfo {
workdir : workdir ,
workflowPath : workflowPath ,
eventName : "workflow_dispatch" ,
errorMessage : "" ,
platforms : platforms ,
}
inputs := map [ string ] string {
"SOME_INPUT" : "input" ,
}
tjfi . runTest ( context . Background ( ) , t , & Config { Inputs : inputs } )
}
2020-03-06 16:17:57 -06:00
func TestRunEventPullRequest ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
workflowPath := "pull-request"
2022-04-05 10:41:36 -05:00
tjfi := TestJobFileInfo {
workdir : workdir ,
workflowPath : workflowPath ,
eventName : "pull_request" ,
errorMessage : "" ,
platforms : platforms ,
2020-03-06 16:17:57 -06:00
}
2022-04-05 10:41:36 -05:00
tjfi . runTest ( context . Background ( ) , t , & Config { EventPath : filepath . Join ( workdir , workflowPath , "event.json" ) } )
2020-03-06 16:17:57 -06:00
}
2023-03-19 12:25:55 -05:00
func TestRunMatrixWithUserDefinedInclusions ( t * testing . T ) {
if testing . Short ( ) {
t . Skip ( "skipping integration test" )
}
workflowPath := "matrix-with-user-inclusions"
tjfi := TestJobFileInfo {
workdir : workdir ,
workflowPath : workflowPath ,
eventName : "push" ,
errorMessage : "" ,
platforms : platforms ,
}
matrix := map [ string ] map [ string ] bool {
"node" : {
"8" : true ,
"8.x" : true ,
} ,
"os" : {
"ubuntu-18.04" : true ,
} ,
}
tjfi . runTest ( context . Background ( ) , t , & Config { Matrix : matrix } )
}