diff --git a/docs/docs/20-usage/20-workflow-syntax.md b/docs/docs/20-usage/20-workflow-syntax.md index a3d04da372..3b31893f6e 100644 --- a/docs/docs/20-usage/20-workflow-syntax.md +++ b/docs/docs/20-usage/20-workflow-syntax.md @@ -546,7 +546,8 @@ The workspace can be customized using the workspace block in the YAML file: ``` :::note -Plugins will always have the workspace base at `/woodpecker` +Plugins will always have the workspace base at `/woodpecker`. +If windows is detected as container host os, it's `c:\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 --git a/pipeline/frontend/yaml/compiler/compiler_test.go b/pipeline/frontend/yaml/compiler/compiler_test.go index dadbcd5dd0..875c4c8050 100644 --- a/pipeline/frontend/yaml/compiler/compiler_test.go +++ b/pipeline/frontend/yaml/compiler/compiler_test.go @@ -299,6 +299,51 @@ func TestCompilerCompile(t *testing.T) { backConf: nil, expectedErr: "step 'dummy' depends on unknown step 'not exist'", }, + { + name: "workflow that targets windows as container host", + fronConf: &yaml_types.Workflow{ + Steps: yaml_types.ContainerList{ContainerList: []*yaml_types.Container{{ + Name: "hello powershell", + Image: "mcr.microsoft.com/windows/servercore:ltsc2022", + Commands: []string{"powershell.exe /c 'echo hello'"}, + }}}, + Workspace: yaml_types.Workspace{ + Base: "c:\\tmp", + }, + }, + backConf: &backend_types.Config{ + Stages: []*backend_types.Stage{ + { + Steps: []*backend_types.Step{{ + Name: "clone", + Type: backend_types.StepTypeClone, + Image: constant.DefaultClonePlugin, + OnSuccess: true, + Failure: "fail", + Volumes: []string{"test_default:c:\\woodpecker"}, + WorkingDir: "c:\\woodpecker/src/github.com/octocat/hello-world", + WorkspaceBase: `c:\woodpecker`, + Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"clone"}}}, + ExtraHosts: []backend_types.HostAlias{}, + }}, + }, {Steps: []*backend_types.Step{{ + Name: "hello powershell", + Type: backend_types.StepTypeCommands, + Image: "mcr.microsoft.com/windows/servercore:ltsc2022", + Commands: []string{"powershell.exe /c 'echo hello'"}, + OnSuccess: true, + Failure: "fail", + Volumes: []string{"test_default:c:\\tmp"}, + WorkingDir: "c:\\tmp/src/github.com/octocat/hello-world", + WorkspaceBase: `c:\tmp`, + Networks: []backend_types.Conn{{Name: "test_default", Aliases: []string{"hello powershell"}}}, + ExtraHosts: []backend_types.HostAlias{}, + }}}, + }, + Networks: defaultNetworks, + Volumes: defaultVolumes, + }, + }, } for _, test := range tests { diff --git a/pipeline/frontend/yaml/compiler/convert.go b/pipeline/frontend/yaml/compiler/convert.go index b31747aa18..66073f4dd0 100644 --- a/pipeline/frontend/yaml/compiler/convert.go +++ b/pipeline/frontend/yaml/compiler/convert.go @@ -18,6 +18,7 @@ import ( "fmt" "maps" "path" + "regexp" "strconv" "strings" @@ -33,10 +34,28 @@ import ( const ( // The pluginWorkspaceBase should not be changed, only if you are sure what you do. pluginWorkspaceBase = "/woodpecker" + // The pluginWorkspaceBaseWindows is like pluginWorkspaceBase but used if we detect windows as container host target. + pluginWorkspaceBaseWindows = `c:\woodpecker` // DefaultWorkspaceBase is set if not altered by the user. DefaultWorkspaceBase = pluginWorkspaceBase ) +var workspaceHasWindowsPattern = regexp.MustCompile(`^[a-zA-Z]:\\`) + +func (c *Compiler) checkRunOnWindowsHeuristics() bool { + // if user customized the workspace witch indicates it targets windows + if workspaceHasWindowsPattern.MatchString(c.workspaceBase) { + return true + } + + // if the platform filter targets windows + if strings.HasPrefix(strings.ToLower(c.metadata.Sys.Platform), "windows") { + return true + } + + return false +} + func (c *Compiler) createProcess(container *yaml_types.Container, stepType backend_types.StepType) (*backend_types.Step, error) { var ( uuid = ulid.Make() @@ -51,7 +70,11 @@ func (c *Compiler) createProcess(container *yaml_types.Container, stepType backe workspaceBase := c.workspaceBase if container.IsPlugin() { // plugins have a predefined workspace base to not tamper with entrypoint executables - workspaceBase = pluginWorkspaceBase + if c.checkRunOnWindowsHeuristics() { + workspaceBase = pluginWorkspaceBaseWindows + } else { + workspaceBase = pluginWorkspaceBase + } } workspaceVolume := fmt.Sprintf("%s_default:%s", c.prefix, workspaceBase) @@ -206,7 +229,12 @@ func (c *Compiler) stepWorkingDir(container *yaml_types.Container) string { } base := c.workspaceBase if container.IsPlugin() { - base = pluginWorkspaceBase + // plugins have a predefined workspace base to not tamper with entrypoint executables + if c.checkRunOnWindowsHeuristics() { + base = pluginWorkspaceBaseWindows + } else { + base = pluginWorkspaceBase + } } return path.Join(base, c.workspacePath, container.Directory) }