diff --git a/README.md b/README.md index a039fa5..8374e15 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ It will save that information to `~/.actrc`, please refer to [Configuration](#co ```none -a, --actor string user that triggered the event (default "nektos/act") -b, --bind bind working directory to container, rather than copy - --container-architecture string Architecture which should be used to run containers, e.g.: linux/amd64. Defaults to linux/ [linux/amd64] + --container-architecture string Architecture which should be used to run containers, e.g.: linux/amd64. Defaults to linux/ [linux/amd64]. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms. --defaultbranch string the name of the main branch --detect-event Use first event type from workflow as event that triggered the workflow -C, --directory string working directory (default ".") diff --git a/cmd/root.go b/cmd/root.go index 8169146..0468b82 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -58,7 +58,7 @@ func Execute(ctx context.Context, version string) { rootCmd.PersistentFlags().StringVarP(&input.secretfile, "secret-file", "", ".secrets", "file with list of secrets to read from (e.g. --secret-file .secrets)") rootCmd.PersistentFlags().BoolVarP(&input.insecureSecrets, "insecure-secrets", "", false, "NOT RECOMMENDED! Doesn't hide secrets while printing logs.") rootCmd.PersistentFlags().StringVarP(&input.envfile, "env-file", "", ".env", "environment file to read and use as env in the containers") - rootCmd.PersistentFlags().StringVarP(&input.containerArchitecture, "container-architecture", "", "", "Architecture which should be used to run containers, e.g.: linux/amd64. Defaults to linux/ [linux/"+runtime.GOARCH+"]") + rootCmd.PersistentFlags().StringVarP(&input.containerArchitecture, "container-architecture", "", "", "Architecture which should be used to run containers, e.g.: linux/amd64. Defaults to linux/ [linux/"+runtime.GOARCH+"]. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms.") rootCmd.SetArgs(args()) if err := rootCmd.Execute(); err != nil { diff --git a/go.mod b/go.mod index 5a73f97..a2ffe39 100644 --- a/go.mod +++ b/go.mod @@ -4,6 +4,7 @@ go 1.14 require ( github.com/AlecAivazis/survey/v2 v2.2.7 + github.com/Masterminds/semver v1.5.0 github.com/MichaelTJones/walk v0.0.0-20161122175330-4748e29d5718 // indirect github.com/andreaskoch/go-fswatch v1.0.0 github.com/containerd/containerd v1.4.1 // indirect diff --git a/go.sum b/go.sum index b2da1d9..c3c0321 100644 --- a/go.sum +++ b/go.sum @@ -93,6 +93,7 @@ github.com/Djarvur/go-err113 v0.0.0-20200410182137-af658d038157/go.mod h1:4UJr5H github.com/Djarvur/go-err113 v0.1.0/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs= github.com/GoogleCloudPlatform/cloudsql-proxy v0.0.0-20191009163259-e802c2cb94ae/go.mod h1:mjwGPas4yKduTyubHvD1Atl9r1rUq8DfVy+gkVvZ+oo= github.com/GoogleCloudPlatform/k8s-cloud-provider v0.0.0-20190822182118-27a4ced34534/go.mod h1:iroGtC8B3tQiqtds1l+mgk/BBOrxbqjH+eUfFQYRc14= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/semver/v3 v3.0.3/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Masterminds/semver/v3 v3.1.0/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= diff --git a/pkg/container/docker_run.go b/pkg/container/docker_run.go index 68340ef..0bbe42d 100644 --- a/pkg/container/docker_run.go +++ b/pkg/container/docker_run.go @@ -26,6 +26,7 @@ import ( "github.com/docker/docker/pkg/stdcopy" specs "github.com/opencontainers/image-spec/specs-go/v1" + "github.com/Masterminds/semver" "github.com/pkg/errors" log "github.com/sirupsen/logrus" "golang.org/x/term" @@ -77,6 +78,25 @@ func NewContainer(input *NewContainerInput) Container { return cr } +// supportsContainerImagePlatform returns true if the underlying Docker server +// API version is 1.41 and beyond +func supportsContainerImagePlatform(cli *client.Client) bool { + ctx := context.TODO() + logger := common.Logger(ctx) + ver, err := cli.ServerVersion(ctx) + if err != nil { + logger.Panicf("Failed to get Docker API Version: %s", err) + return false + } + sv, err := semver.NewVersion(ver.APIVersion) + if err != nil { + logger.Panicf("Failed to unmarshal Docker Version: %s", err) + return false + } + constraint, _ := semver.NewConstraint(">= 1.41") + return constraint.Check(sv) +} + func (cr *containerReference) Create() common.Executor { return common. NewDebugExecutor("%sdocker create image=%s platform=%s entrypoint=%+q cmd=%+q", logPrefix, cr.input.Image, cr.input.Platform, cr.input.Entrypoint, cr.input.Cmd). @@ -272,22 +292,26 @@ func (cr *containerReference) create() common.Executor { }) } - desiredPlatform := strings.SplitN(cr.input.Platform, `/`, 2) + var platSpecs *specs.Platform + if supportsContainerImagePlatform(cr.cli) { + desiredPlatform := strings.SplitN(cr.input.Platform, `/`, 2) - if len(desiredPlatform) != 2 { - logger.Panicf("Incorrect container platform option. %s is not a valid platform.", cr.input.Platform) + if len(desiredPlatform) != 2 { + logger.Panicf("Incorrect container platform option. %s is not a valid platform.", cr.input.Platform) + } + + platSpecs = &specs.Platform{ + Architecture: desiredPlatform[1], + OS: desiredPlatform[0], + } } - resp, err := cr.cli.ContainerCreate(ctx, config, &container.HostConfig{ Binds: input.Binds, Mounts: mounts, NetworkMode: container.NetworkMode(input.NetworkMode), Privileged: input.Privileged, UsernsMode: container.UsernsMode(input.UsernsMode), - }, nil, &specs.Platform{ - Architecture: desiredPlatform[1], - OS: desiredPlatform[0], - }, input.Name) + }, nil, platSpecs, input.Name) if err != nil { return errors.WithStack(err) }