Skip to content

Commit

Permalink
Support custom steps entrypoint (#2985)
Browse files Browse the repository at this point in the history
Closes #278

---------

Co-authored-by: Anbraten <anton@ju60.de>
Co-authored-by: 6543 <6543@obermui.de>
  • Loading branch information
3 people authored Jan 19, 2024
1 parent 9f215ab commit d1d2e97
Show file tree
Hide file tree
Showing 11 changed files with 32 additions and 10 deletions.
4 changes: 4 additions & 0 deletions docs/docs/20-usage/20-workflow-syntax.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions pipeline/backend/common/script.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions pipeline/backend/common/script_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
5 changes: 4 additions & 1 deletion pipeline/backend/docker/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
5 changes: 4 additions & 1 deletion pipeline/backend/kubernetes/pod.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Expand Down
1 change: 1 addition & 0 deletions pipeline/backend/kubernetes/pod_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions pipeline/backend/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion pipeline/frontend/metadata/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

package metadata

// Event types corresponding to scm hooks.
// Event types corresponding to forge hooks.
const (
EventPush = "push"
EventPull = "pull_request"
Expand Down
1 change: 1 addition & 0 deletions pipeline/frontend/yaml/compiler/convert.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
5 changes: 3 additions & 2 deletions pipeline/frontend/yaml/types/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
8 changes: 8 additions & 0 deletions pipeline/frontend/yaml/types/container_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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())
}

0 comments on commit d1d2e97

Please sign in to comment.