mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2025-08-12 04:22:29 +00:00
Add "plugin" support to local backend (#2239)
and a nit to improve the clone --- *Sponsored by Kithara Software GmbH*
This commit is contained in:
parent
7adfa6c2ba
commit
3954d85a5b
@ -6,11 +6,7 @@ The local backend will execute the pipelines on the local system without any iso
|
|||||||
|
|
||||||
:::note
|
:::note
|
||||||
This backend is still pretty new and can not be treated as stable. Its
|
This backend is still pretty new and can not be treated as stable. Its
|
||||||
implementation and configuration can change at any time. Binary releases of the
|
implementation and configuration can change at any time.
|
||||||
agent will be available with the release of the [1.0.0
|
|
||||||
milestone](https://github.com/woodpecker-ci/woodpecker/milestone/4), so for now
|
|
||||||
you must compile the agent by yourself, to get the local backend functionality.
|
|
||||||
<!-- TODO: remove the self-compile note after the release of the agent -->
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
Since the code runs directly in the same context as the agent (same user, same
|
Since the code runs directly in the same context as the agent (same user, same
|
||||||
@ -92,6 +88,17 @@ steps:
|
|||||||
[...]
|
[...]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Plugins as Executable Binaries
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
steps:
|
||||||
|
build:
|
||||||
|
image: /usr/bin/tree
|
||||||
|
```
|
||||||
|
|
||||||
|
If no commands are provided, we treat them as plugins in the usual manner.
|
||||||
|
In the context of the local backend, plugins are simply executable binaries, which can be located using their name if they are listed in `$PATH`, or through an absolute path.
|
||||||
|
|
||||||
### Using labels to filter tasks
|
### Using labels to filter tasks
|
||||||
|
|
||||||
You can use the [agent configuration
|
You can use the [agent configuration
|
||||||
|
@ -6,11 +6,7 @@ The local backend will execute the pipelines on the local system without any iso
|
|||||||
|
|
||||||
:::note
|
:::note
|
||||||
This backend is still pretty new and can not be treated as stable. Its
|
This backend is still pretty new and can not be treated as stable. Its
|
||||||
implementation and configuration can change at any time. Binary releases of the
|
implementation and configuration can change at any time.
|
||||||
agent will be available with the release of the [1.0.0
|
|
||||||
milestone](https://github.com/woodpecker-ci/woodpecker/milestone/4), so for now
|
|
||||||
you must compile the agent by yourself, to get the local backend functionality.
|
|
||||||
<!-- TODO: remove the self-compile note after the release of the agent -->
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
Since the code runs directly in the same context as the agent (same user, same
|
Since the code runs directly in the same context as the agent (same user, same
|
||||||
|
@ -28,7 +28,6 @@ import (
|
|||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
"github.com/woodpecker-ci/woodpecker/pipeline/backend/types"
|
||||||
"github.com/woodpecker-ci/woodpecker/shared/constant"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// checkGitCloneCap check if we have the git binary on hand
|
// checkGitCloneCap check if we have the git binary on hand
|
||||||
@ -64,17 +63,20 @@ func (e *local) setupClone(state *workflowState) error {
|
|||||||
|
|
||||||
// execClone executes a clone-step locally
|
// execClone executes a clone-step locally
|
||||||
func (e *local) execClone(ctx context.Context, step *types.Step, state *workflowState, env []string) error {
|
func (e *local) execClone(ctx context.Context, step *types.Step, state *workflowState, env []string) error {
|
||||||
if err := e.setupClone(state); err != nil {
|
if scm := step.Environment["CI_REPO_SCM"]; scm != "git" {
|
||||||
return fmt.Errorf("setup clone step failed: %w", err)
|
return fmt.Errorf("local backend can only clone from git repos, but this repo use '%s'", scm)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := checkGitCloneCap(); err != nil {
|
if err := checkGitCloneCap(); err != nil {
|
||||||
return fmt.Errorf("check for git clone capabilities failed: %w", err)
|
return fmt.Errorf("check for git clone capabilities failed: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if step.Image != constant.DefaultCloneImage {
|
if err := e.setupClone(state); err != nil {
|
||||||
// TODO: write message into log
|
return fmt.Errorf("setup clone step failed: %w", err)
|
||||||
log.Warn().Msgf("clone step image '%s' does not match default git clone image. We ignore it assume git.", step.Image)
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(step.Image, "plugin-git") {
|
||||||
|
log.Warn().Msgf("clone step image '%s' does not match default git clone image. We ignore it and use our plugin-git anyway.", step.Image)
|
||||||
}
|
}
|
||||||
|
|
||||||
rmCmd, err := writeNetRC(step, state)
|
rmCmd, err := writeNetRC(step, state)
|
||||||
|
@ -120,13 +120,16 @@ func (e *local) StartStep(ctx context.Context, step *types.Step, taskUUID string
|
|||||||
return e.execClone(ctx, step, state, env)
|
return e.execClone(ctx, step, state, env)
|
||||||
case types.StepTypeCommands:
|
case types.StepTypeCommands:
|
||||||
return e.execCommands(ctx, step, state, env)
|
return e.execCommands(ctx, step, state, env)
|
||||||
|
case types.StepTypePlugin:
|
||||||
|
return e.execPlugin(ctx, step, state, env)
|
||||||
default:
|
default:
|
||||||
return ErrUnsupportedStepType
|
return ErrUnsupportedStepType
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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 {
|
func (e *local) execCommands(ctx context.Context, step *types.Step, state *workflowState, env []string) error {
|
||||||
// TODO: use commands directly
|
// TODO: find a way to simulate commands to be exec as stdin user commands instead of generating a script and hope the shell understands
|
||||||
script := ""
|
script := ""
|
||||||
for _, cmd := range step.Commands {
|
for _, cmd := range step.Commands {
|
||||||
script += fmt.Sprintf("echo + %s\n%s\n", strings.TrimSpace(shellescape.Quote(cmd)), cmd)
|
script += fmt.Sprintf("echo + %s\n%s\n", strings.TrimSpace(shellescape.Quote(cmd)), cmd)
|
||||||
@ -148,6 +151,26 @@ func (e *local) execCommands(ctx context.Context, step *types.Step, state *workf
|
|||||||
return cmd.Start()
|
return cmd.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// execPlugin use step.Image as exec binary
|
||||||
|
func (e *local) execPlugin(ctx context.Context, step *types.Step, state *workflowState, env []string) error {
|
||||||
|
binary, err := exec.LookPath(step.Image)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("lookup plugin binary: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.CommandContext(ctx, binary)
|
||||||
|
cmd.Env = env
|
||||||
|
cmd.Dir = state.workspaceDir
|
||||||
|
|
||||||
|
// Get output and redirect Stderr to Stdout
|
||||||
|
e.output, _ = cmd.StdoutPipe()
|
||||||
|
cmd.Stderr = cmd.Stdout
|
||||||
|
|
||||||
|
state.stepCMDs[step.Name] = cmd
|
||||||
|
|
||||||
|
return cmd.Start()
|
||||||
|
}
|
||||||
|
|
||||||
// WaitStep for the pipeline step to complete and returns
|
// WaitStep for the pipeline step to complete and returns
|
||||||
// the completion results.
|
// the completion results.
|
||||||
func (e *local) WaitStep(_ context.Context, step *types.Step, taskUUID string) (*types.State, error) {
|
func (e *local) WaitStep(_ context.Context, step *types.Step, taskUUID string) (*types.State, error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user