mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-25 19:31:05 +00:00
Make sure plugins only mount the workspace base in a predefinde location (#3933)
This commit is contained in:
parent
d6e3ebf051
commit
764329ed1d
10 changed files with 75 additions and 24 deletions
|
@ -523,7 +523,9 @@ For more details check the [services docs](./60-services.md).
|
|||
|
||||
## `workspace`
|
||||
|
||||
The workspace defines the shared volume and working directory shared by all workflow steps. The default workspace matches the pattern `/woodpecker/src/github.com/octocat/hello-world`, based on your repository URL.
|
||||
The workspace defines the shared volume and working directory shared by all workflow steps.
|
||||
The default workspace base is `/woodpecker` and the path is extended with the repository URL (`src/{url-without-schema}`).
|
||||
So an example would be `/woodpecker/src/github.com/octocat/hello-world`.
|
||||
|
||||
The workspace can be customized using the workspace block in the YAML file:
|
||||
|
||||
|
@ -540,6 +542,10 @@ The workspace can be customized using the workspace block in the YAML file:
|
|||
- go test
|
||||
```
|
||||
|
||||
:::note
|
||||
Plugins will always have the workspace base at `/woodpecker`
|
||||
:::
|
||||
|
||||
The base attribute defines a shared base volume available to all steps. This ensures your source code, dependencies and compiled binaries are persisted and shared between steps.
|
||||
|
||||
```diff
|
||||
|
|
|
@ -47,6 +47,11 @@ steps:
|
|||
## Plugin Isolation
|
||||
|
||||
Plugins are just pipeline steps. They share the build workspace, mounted as a volume, and therefore have access to your source tree.
|
||||
While normal steps are all about arbitrary code execution, plugins should only allow the functions intended by the plugin author.
|
||||
|
||||
So there are a few limitations, like the workspace base is always mounted at `/woodpecker`, but the working directory is dynamically adjusted acordingly. So as user of a plugin you should not have to care about this.
|
||||
|
||||
Also instead of using environment variables the plugin should only care about one prefixed with `PLUGIN_` witch are the internaml representation of the **settings** ([read more](./20-creating-plugins.md)).
|
||||
|
||||
## Finding Plugins
|
||||
|
||||
|
|
|
@ -523,7 +523,9 @@ For more details check the [services docs](./60-services.md).
|
|||
|
||||
## `workspace`
|
||||
|
||||
The workspace defines the shared volume and working directory shared by all workflow steps. The default workspace matches the pattern `/woodpecker/src/github.com/octocat/hello-world`, based on your repository URL.
|
||||
The workspace defines the shared volume and working directory shared by all workflow steps.
|
||||
The default workspace base is `/woodpecker` and the path is extended with the repository URL (`src/{url-without-schema}`).
|
||||
So an example would be `/woodpecker/src/github.com/octocat/hello-world`.
|
||||
|
||||
The workspace can be customized using the workspace block in the YAML file:
|
||||
|
||||
|
@ -540,6 +542,10 @@ The workspace can be customized using the workspace block in the YAML file:
|
|||
- go test
|
||||
```
|
||||
|
||||
:::note
|
||||
Plugins will always have the workspace base at `/woodpecker`
|
||||
:::
|
||||
|
||||
The base attribute defines a shared base volume available to all steps. This ensures your source code, dependencies and compiled binaries are persisted and shared between steps.
|
||||
|
||||
```diff
|
||||
|
|
|
@ -47,6 +47,11 @@ steps:
|
|||
## Plugin Isolation
|
||||
|
||||
Plugins are just pipeline steps. They share the build workspace, mounted as a volume, and therefore have access to your source tree.
|
||||
While normal steps are all about arbitrary code execution, plugins should only allow the functions intended by the plugin author.
|
||||
|
||||
So there are a few limitations, like the workspace base is always mounted at `/woodpecker`, but the working directory is dynamically adjusted acordingly. So as user of a plugin you should not have to care about this.
|
||||
|
||||
Also instead of using environment variables the plugin should only care about one prefixed with `PLUGIN_` witch are the internaml representation of the **settings** ([read more](./20-creating-plugins.md)).
|
||||
|
||||
## Finding Plugins
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ package compiler
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
backend_types "go.woodpecker-ci.org/woodpecker/v2/pipeline/backend/types"
|
||||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/metadata"
|
||||
|
@ -98,8 +99,8 @@ type Compiler struct {
|
|||
networks []string
|
||||
env map[string]string
|
||||
cloneEnv map[string]string
|
||||
base string
|
||||
path string
|
||||
workspaceBase string
|
||||
workspacePath string
|
||||
metadata metadata.Metadata
|
||||
registries []Registry
|
||||
secrets map[string]Secret
|
||||
|
@ -156,10 +157,10 @@ func (c *Compiler) Compile(conf *yaml_types.Workflow) (*backend_types.Config, er
|
|||
// overrides the default workspace paths when specified
|
||||
// in the YAML file.
|
||||
if len(conf.Workspace.Base) != 0 {
|
||||
c.base = conf.Workspace.Base
|
||||
c.workspaceBase = path.Clean(conf.Workspace.Base)
|
||||
}
|
||||
if len(conf.Workspace.Path) != 0 {
|
||||
c.path = conf.Workspace.Path
|
||||
c.workspacePath = path.Clean(conf.Workspace.Path)
|
||||
}
|
||||
|
||||
cloneImage := constant.DefaultCloneImage
|
||||
|
|
|
@ -61,13 +61,14 @@ func TestSecretAvailable(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCompilerCompile(t *testing.T) {
|
||||
repoURL := "https://github.com/octocat/hello-world"
|
||||
compiler := New(
|
||||
WithMetadata(metadata.Metadata{
|
||||
Repo: metadata.Repo{
|
||||
Owner: "octacat",
|
||||
Name: "hello-world",
|
||||
Private: true,
|
||||
ForgeURL: "https://github.com/octocat/hello-world",
|
||||
ForgeURL: repoURL,
|
||||
CloneURL: "https://github.com/octocat/hello-world.git",
|
||||
},
|
||||
}),
|
||||
|
@ -76,6 +77,8 @@ func TestCompilerCompile(t *testing.T) {
|
|||
"COLORED": "true",
|
||||
}),
|
||||
WithPrefix("test"),
|
||||
// we use "/test" as custom workspace base to ensure the enforcement of the pluginWorkspaceBase is applied
|
||||
WithWorkspaceFromURL("/test", repoURL),
|
||||
)
|
||||
|
||||
defaultNetworks := []*backend_types.Network{{
|
||||
|
@ -92,7 +95,8 @@ func TestCompilerCompile(t *testing.T) {
|
|||
Image: constant.DefaultCloneImage,
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Volumes: []string{defaultVolumes[0].Name + ":/woodpecker"},
|
||||
WorkingDir: "/woodpecker/src/github.com/octocat/hello-world",
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"clone"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}},
|
||||
|
@ -137,7 +141,8 @@ func TestCompilerCompile(t *testing.T) {
|
|||
Image: "dummy_img",
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Volumes: []string{defaultVolumes[0].Name + ":/woodpecker"},
|
||||
WorkingDir: "/woodpecker/src/github.com/octocat/hello-world",
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"dummy"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}},
|
||||
|
@ -172,7 +177,8 @@ func TestCompilerCompile(t *testing.T) {
|
|||
Commands: []string{"env"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Volumes: []string{defaultVolumes[0].Name + ":/test"},
|
||||
WorkingDir: "/test/src/github.com/octocat/hello-world",
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo env"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}},
|
||||
|
@ -184,7 +190,8 @@ func TestCompilerCompile(t *testing.T) {
|
|||
Commands: []string{"echo 1"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Volumes: []string{defaultVolumes[0].Name + ":/test"},
|
||||
WorkingDir: "/test/src/github.com/octocat/hello-world",
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 1"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}, {
|
||||
|
@ -194,7 +201,8 @@ func TestCompilerCompile(t *testing.T) {
|
|||
Commands: []string{"echo 2"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Volumes: []string{defaultVolumes[0].Name + ":/test"},
|
||||
WorkingDir: "/test/src/github.com/octocat/hello-world",
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"parallel echo 2"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}},
|
||||
|
@ -228,7 +236,8 @@ func TestCompilerCompile(t *testing.T) {
|
|||
Commands: []string{"env"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Volumes: []string{defaultVolumes[0].Name + ":/test"},
|
||||
WorkingDir: "/test/src/github.com/octocat/hello-world",
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo env"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}, {
|
||||
|
@ -238,7 +247,8 @@ func TestCompilerCompile(t *testing.T) {
|
|||
Commands: []string{"echo 2"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Volumes: []string{defaultVolumes[0].Name + ":/test"},
|
||||
WorkingDir: "/test/src/github.com/octocat/hello-world",
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo 2"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}},
|
||||
|
@ -250,7 +260,8 @@ func TestCompilerCompile(t *testing.T) {
|
|||
Commands: []string{"echo 1"},
|
||||
OnSuccess: true,
|
||||
Failure: "fail",
|
||||
Volumes: []string{defaultVolumes[0].Name + ":"},
|
||||
Volumes: []string{defaultVolumes[0].Name + ":/test"},
|
||||
WorkingDir: "/test/src/github.com/octocat/hello-world",
|
||||
Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"echo 1"}}},
|
||||
ExtraHosts: []backend_types.HostAlias{},
|
||||
}},
|
||||
|
|
|
@ -30,6 +30,13 @@ import (
|
|||
"go.woodpecker-ci.org/woodpecker/v2/pipeline/frontend/yaml/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
// The pluginWorkspaceBase should not be changed, only if you are sure what you do.
|
||||
pluginWorkspaceBase = "/woodpecker"
|
||||
// DefaultWorkspaceBase is set if not altered by the user.
|
||||
DefaultWorkspaceBase = pluginWorkspaceBase
|
||||
)
|
||||
|
||||
func (c *Compiler) createProcess(container *yaml_types.Container, stepType backend_types.StepType) (*backend_types.Step, error) {
|
||||
var (
|
||||
uuid = ulid.Make()
|
||||
|
@ -37,11 +44,17 @@ func (c *Compiler) createProcess(container *yaml_types.Container, stepType backe
|
|||
detached bool
|
||||
workingDir string
|
||||
|
||||
workspace = fmt.Sprintf("%s_default:%s", c.prefix, c.base)
|
||||
privileged = container.Privileged
|
||||
networkMode = container.NetworkMode
|
||||
)
|
||||
|
||||
workspaceBase := c.workspaceBase
|
||||
if container.IsPlugin() {
|
||||
// plugins have a predefined workspace base to not tamper with entrypoint executables
|
||||
workspaceBase = pluginWorkspaceBase
|
||||
}
|
||||
workspaceVolume := fmt.Sprintf("%s_default:%s", c.prefix, workspaceBase)
|
||||
|
||||
networks := []backend_types.Conn{
|
||||
{
|
||||
Name: fmt.Sprintf("%s_default", c.prefix),
|
||||
|
@ -66,7 +79,7 @@ func (c *Compiler) createProcess(container *yaml_types.Container, stepType backe
|
|||
|
||||
var volumes []string
|
||||
if !c.local {
|
||||
volumes = append(volumes, workspace)
|
||||
volumes = append(volumes, workspaceVolume)
|
||||
}
|
||||
volumes = append(volumes, c.volumes...)
|
||||
for _, volume := range container.Volumes.Volumes {
|
||||
|
@ -77,7 +90,7 @@ func (c *Compiler) createProcess(container *yaml_types.Container, stepType backe
|
|||
environment := map[string]string{}
|
||||
maps.Copy(environment, c.env)
|
||||
|
||||
environment["CI_WORKSPACE"] = path.Join(c.base, c.path)
|
||||
environment["CI_WORKSPACE"] = path.Join(workspaceBase, c.workspacePath)
|
||||
|
||||
if stepType == backend_types.StepTypeService || container.Detached {
|
||||
detached = true
|
||||
|
@ -219,7 +232,11 @@ func (c *Compiler) stepWorkingDir(container *yaml_types.Container) string {
|
|||
if path.IsAbs(container.Directory) {
|
||||
return container.Directory
|
||||
}
|
||||
return path.Join(c.base, c.path, container.Directory)
|
||||
base := c.workspaceBase
|
||||
if container.IsPlugin() {
|
||||
base = pluginWorkspaceBase
|
||||
}
|
||||
return path.Join(base, c.workspacePath, container.Directory)
|
||||
}
|
||||
|
||||
func convertPort(portDef string) (backend_types.Port, error) {
|
||||
|
|
|
@ -97,8 +97,8 @@ func WithNetrc(username, password, machine string) Option {
|
|||
// plugin steps in the pipeline.
|
||||
func WithWorkspace(base, path string) Option {
|
||||
return func(compiler *Compiler) {
|
||||
compiler.base = base
|
||||
compiler.path = path
|
||||
compiler.workspaceBase = base
|
||||
compiler.workspacePath = path
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,8 +29,8 @@ func TestWithWorkspace(t *testing.T) {
|
|||
"src/github.com/octocat/hello-world",
|
||||
),
|
||||
)
|
||||
assert.Equal(t, "/pipeline", compiler.base)
|
||||
assert.Equal(t, "src/github.com/octocat/hello-world", compiler.path)
|
||||
assert.Equal(t, "/pipeline", compiler.workspaceBase)
|
||||
assert.Equal(t, "src/github.com/octocat/hello-world", compiler.workspacePath)
|
||||
}
|
||||
|
||||
func TestWithEscalated(t *testing.T) {
|
||||
|
|
|
@ -291,7 +291,7 @@ func (b *StepBuilder) toInternalRepresentation(parsed *yaml_types.Workflow, envi
|
|||
),
|
||||
),
|
||||
compiler.WithProxy(b.ProxyOpts),
|
||||
compiler.WithWorkspaceFromURL("/woodpecker", b.Repo.ForgeURL),
|
||||
compiler.WithWorkspaceFromURL(compiler.DefaultWorkspaceBase, b.Repo.ForgeURL),
|
||||
compiler.WithMetadata(metadata),
|
||||
compiler.WithTrusted(b.Repo.IsTrusted),
|
||||
compiler.WithNetrcOnlyTrusted(b.Repo.NetrcOnlyTrusted),
|
||||
|
|
Loading…
Reference in a new issue