diff --git a/docs/docs/30-administration/22-backends/20-local.md b/docs/docs/30-administration/22-backends/20-local.md index 5e57b04ce..b03dd0bc1 100644 --- a/docs/docs/30-administration/22-backends/20-local.md +++ b/docs/docs/30-administration/22-backends/20-local.md @@ -6,11 +6,7 @@ The local backend will execute the pipelines on the local system without any iso :::note 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 -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. - +implementation and configuration can change at any time. ::: 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 You can use the [agent configuration diff --git a/docs/versioned_docs/version-1.0/30-administration/22-backends/20-local.md b/docs/versioned_docs/version-1.0/30-administration/22-backends/20-local.md index 5e57b04ce..c8c5dbc4e 100644 --- a/docs/versioned_docs/version-1.0/30-administration/22-backends/20-local.md +++ b/docs/versioned_docs/version-1.0/30-administration/22-backends/20-local.md @@ -6,11 +6,7 @@ The local backend will execute the pipelines on the local system without any iso :::note 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 -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. - +implementation and configuration can change at any time. ::: Since the code runs directly in the same context as the agent (same user, same diff --git a/pipeline/backend/local/clone.go b/pipeline/backend/local/clone.go index 4a76cd26c..72f0d01af 100644 --- a/pipeline/backend/local/clone.go +++ b/pipeline/backend/local/clone.go @@ -28,7 +28,6 @@ import ( "github.com/rs/zerolog/log" "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 @@ -64,17 +63,20 @@ func (e *local) setupClone(state *workflowState) error { // execClone executes a clone-step locally func (e *local) execClone(ctx context.Context, step *types.Step, state *workflowState, env []string) error { - if err := e.setupClone(state); err != nil { - return fmt.Errorf("setup clone step failed: %w", err) + if scm := step.Environment["CI_REPO_SCM"]; scm != "git" { + return fmt.Errorf("local backend can only clone from git repos, but this repo use '%s'", scm) } if err := checkGitCloneCap(); err != nil { return fmt.Errorf("check for git clone capabilities failed: %w", err) } - if step.Image != constant.DefaultCloneImage { - // TODO: write message into log - log.Warn().Msgf("clone step image '%s' does not match default git clone image. We ignore it assume git.", step.Image) + if err := e.setupClone(state); err != nil { + return fmt.Errorf("setup clone step failed: %w", err) + } + + 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) diff --git a/pipeline/backend/local/local.go b/pipeline/backend/local/local.go index 88050bc5f..998405711 100644 --- a/pipeline/backend/local/local.go +++ b/pipeline/backend/local/local.go @@ -120,13 +120,16 @@ func (e *local) StartStep(ctx context.Context, step *types.Step, taskUUID string return e.execClone(ctx, step, state, env) case types.StepTypeCommands: return e.execCommands(ctx, step, state, env) + case types.StepTypePlugin: + return e.execPlugin(ctx, step, state, env) default: 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 { - // 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 := "" for _, cmd := range step.Commands { 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() } +// 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 // the completion results. func (e *local) WaitStep(_ context.Context, step *types.Step, taskUUID string) (*types.State, error) {