Support custom steps entrypoint (#2985)

Closes https://github.com/woodpecker-ci/woodpecker/issues/278

---------

Co-authored-by: Anbraten <anton@ju60.de>
Co-authored-by: 6543 <6543@obermui.de>
This commit is contained in:
qwerty287 2024-01-19 05:34:02 +01:00 committed by GitHub
parent 9f215ab932
commit d1d2e9723d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 32 additions and 10 deletions

View file

@ -156,6 +156,10 @@ docker run --entrypoint=build.sh golang
Only build steps can define commands. You cannot use commands with plugins or services.
:::
### `entrypoint`
Allows you to specify the entrypoint for containers. Note that this must be a list of the command and its arguments (e.g. `["/bin/sh", "-c"]`).
### `environment`
Woodpecker provides the ability to pass environment variables to individual steps.

View file

@ -18,20 +18,20 @@ import (
"encoding/base64"
)
func GenerateContainerConf(commands []string, goos string) (env map[string]string, entry, cmd []string) {
func GenerateContainerConf(commands []string, goos string) (env map[string]string, entry []string, cmd string) {
env = make(map[string]string)
if goos == "windows" {
env["CI_SCRIPT"] = base64.StdEncoding.EncodeToString([]byte(generateScriptWindows(commands)))
env["HOME"] = "c:\\root"
env["SHELL"] = "powershell.exe"
entry = []string{"powershell", "-noprofile", "-noninteractive", "-command"}
cmd = []string{"[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Env:CI_SCRIPT)) | iex"}
cmd = "[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Env:CI_SCRIPT)) | iex"
} else {
env["CI_SCRIPT"] = base64.StdEncoding.EncodeToString([]byte(generateScriptPosix(commands)))
env["HOME"] = "/root"
env["SHELL"] = "/bin/sh"
entry = []string{"/bin/sh", "-c"}
cmd = []string{"echo $CI_SCRIPT | base64 -d | /bin/sh -e"}
cmd = "echo $CI_SCRIPT | base64 -d | /bin/sh -e"
}
return env, entry, cmd

View file

@ -17,11 +17,11 @@ func TestGenerateContainerConf(t *testing.T) {
assert.Equal(t, "c:\\root", gotEnv["HOME"])
assert.Equal(t, "powershell.exe", gotEnv["SHELL"])
assert.Equal(t, []string{"powershell", "-noprofile", "-noninteractive", "-command"}, gotEntry)
assert.Equal(t, []string{"[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Env:CI_SCRIPT)) | iex"}, gotCmd)
assert.Equal(t, "[System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($Env:CI_SCRIPT)) | iex", gotCmd)
gotEnv, gotEntry, gotCmd = GenerateContainerConf([]string{"echo hello world"}, "linux")
assert.Equal(t, posixScriptBase64, gotEnv["CI_SCRIPT"])
assert.Equal(t, "/root", gotEnv["HOME"])
assert.Equal(t, "/bin/sh", gotEnv["SHELL"])
assert.Equal(t, []string{"/bin/sh", "-c"}, gotEntry)
assert.Equal(t, []string{"echo $CI_SCRIPT | base64 -d | /bin/sh -e"}, gotCmd)
assert.Equal(t, "echo $CI_SCRIPT | base64 -d | /bin/sh -e", gotCmd)
}

View file

@ -47,8 +47,11 @@ func (e *docker) toConfig(step *types.Step) *container.Config {
for k, v := range env {
configEnv[k] = v
}
if len(step.Entrypoint) > 0 {
entry = step.Entrypoint
}
config.Entrypoint = entry
config.Cmd = cmd
config.Cmd = []string{cmd}
}
if len(configEnv) != 0 {

View file

@ -129,8 +129,11 @@ func podContainer(step *types.Step, podName, goos string) (v1.Container, error)
if len(step.Commands) != 0 {
scriptEnv, command, args := common.GenerateContainerConf(step.Commands, goos)
if len(step.Entrypoint) > 0 {
command = step.Entrypoint
}
container.Command = command
container.Args = args
container.Args = []string{args}
maps.Copy(step.Environment, scriptEnv)
}

View file

@ -310,6 +310,7 @@ func TestFullPod(t *testing.T) {
Pull: true,
Privileged: true,
Commands: []string{"go get", "go test"},
Entrypoint: []string{"/bin/sh", "-c"},
Volumes: []string{"woodpecker-cache:/woodpecker/src/cache"},
Environment: map[string]string{"CGO": "0"},
ExtraHosts: hostAliases,

View file

@ -146,6 +146,7 @@ func (e *local) StartStep(ctx context.Context, step *types.Step, taskUUID string
// execCommands use step.Image as shell and run the commands in it
func (e *local) execCommands(ctx context.Context, step *types.Step, state *workflowState, env []string) error {
// Prepare commands
// TODO support `entrypoint` from pipeline config
args, err := e.genCmdByShell(step.Image, step.Commands)
if err != nil {
return fmt.Errorf("could not convert commands into args: %w", err)

View file

@ -14,7 +14,7 @@
package metadata
// Event types corresponding to scm hooks.
// Event types corresponding to forge hooks.
const (
EventPush = "push"
EventPull = "pull_request"

View file

@ -185,6 +185,7 @@ func (c *Compiler) createProcess(container *yaml_types.Container, stepType backe
WorkingDir: workingdir,
Environment: environment,
Commands: container.Commands,
Entrypoint: container.Entrypoint,
ExtraHosts: extraHosts,
Volumes: volumes,
Tmpfs: container.Tmpfs,

View file

@ -35,6 +35,7 @@ type (
Container struct {
BackendOptions BackendOptions `yaml:"backend_options,omitempty"`
Commands base.StringOrSlice `yaml:"commands,omitempty"`
Entrypoint base.StringOrSlice `yaml:"entrypoint,omitempty"`
Detached bool `yaml:"detach,omitempty"`
Directory string `yaml:"directory,omitempty"`
Environment base.SliceOrMap `yaml:"environment,omitempty"`
@ -50,7 +51,7 @@ type (
Ports []string `yaml:"ports,omitempty"`
DependsOn base.StringOrSlice `yaml:"depends_on,omitempty"`
// Docker Specific
// Docker and Kubernetes Specific
Privileged bool `yaml:"privileged,omitempty"`
// Undocumented
@ -119,7 +120,7 @@ func (c *ContainerList) UnmarshalYAML(value *yaml.Node) error {
}
func (c *Container) IsPlugin() bool {
return len(c.Commands) == 0
return len(c.Commands) == 0 && len(c.Entrypoint) == 0
}
func (c *Container) IsTrustedCloneImage() bool {

View file

@ -39,6 +39,7 @@ devices:
directory: example/
dns: 8.8.8.8
dns_search: example.com
entrypoint: [/bin/sh, -c]
environment:
- RACK_ENV=development
- SHOW=true
@ -86,6 +87,7 @@ func TestUnmarshalContainer(t *testing.T) {
Directory: "example/",
DNS: base.StringOrSlice{"8.8.8.8"},
DNSSearch: base.StringOrSlice{"example.com"},
Entrypoint: []string{"/bin/sh", "-c"},
Environment: base.SliceOrMap{"RACK_ENV": "development", "SHOW": "true"},
ExtraHosts: []string{"somehost:162.242.195.82", "otherhost:50.31.209.229", "ipv6:2001:db8::10"},
Image: "golang:latest",
@ -308,4 +310,10 @@ func TestIsPlugin(t *testing.T) {
assert.False(t, (&Container{
Commands: base.StringOrSlice(strslice.StrSlice{"echo 'this is not a plugin'"}),
}).IsPlugin())
assert.True(t, (&Container{
Entrypoint: base.StringOrSlice(strslice.StrSlice{}),
}).IsPlugin())
assert.False(t, (&Container{
Entrypoint: base.StringOrSlice(strslice.StrSlice{"echo 'this is not a plugin'"}),
}).IsPlugin())
}