diff --git a/CHANGELOG.md b/CHANGELOG.md index d46f8fc6da..a0a93b625e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ All notable changes to `src-cli` are documented in this file. ### Removed - Removed `src sbom` and `src signature` commands. SBOMs and container signatures are no longer published as of Sourcegraph 7.1.0. +- Removed the internal `src batch exec` command. Server-side Batch Changes execution now uses the `batch-exec` binary in the Sourcegraph repository. ## 6.7.1104 diff --git a/cmd/src/batch_exec.go b/cmd/src/batch_exec.go deleted file mode 100644 index 11aadf39e3..0000000000 --- a/cmd/src/batch_exec.go +++ /dev/null @@ -1,284 +0,0 @@ -package main - -import ( - "context" - "encoding/json" - "flag" - "fmt" - "io" - "os" - "path/filepath" - "time" - - "github.com/sourcegraph/src-cli/internal/batches/docker" - "github.com/sourcegraph/src-cli/internal/batches/log" - "github.com/sourcegraph/src-cli/internal/batches/repozip" - "github.com/sourcegraph/src-cli/internal/batches/workspace" - - "github.com/sourcegraph/sourcegraph/lib/errors" - - "github.com/sourcegraph/src-cli/internal/batches/executor" - "github.com/sourcegraph/src-cli/internal/batches/graphql" - "github.com/sourcegraph/src-cli/internal/batches/service" - "github.com/sourcegraph/src-cli/internal/batches/ui" - "github.com/sourcegraph/src-cli/internal/cmderrors" - - batcheslib "github.com/sourcegraph/sourcegraph/lib/batches" -) - -const ( - execPullParallelism = 4 -) - -type executorModeFlags struct { - timeout time.Duration - file string - runAsImageUser bool - tempDir string - repoDir string - workspaceFilesDir string - binaryDiffs bool -} - -func newExecutorModeFlags(flagSet *flag.FlagSet) (f *executorModeFlags) { - f = &executorModeFlags{} - flagSet.DurationVar(&f.timeout, "timeout", 60*time.Minute, "The maximum duration a single batch spec step can take.") - flagSet.StringVar(&f.file, "f", "", "The workspace execution input file to read.") - flagSet.BoolVar(&f.runAsImageUser, "run-as-image-user", false, "True to run step containers as the default image user; if false or omitted, containers are always run as root.") - flagSet.StringVar(&f.tempDir, "tmp", "", "Directory for storing temporary data.") - flagSet.StringVar(&f.repoDir, "repo", "", "Path of the checked out repo on disk.") - flagSet.StringVar(&f.workspaceFilesDir, "workspaceFiles", "", "Path of workspace files on disk.") - flagSet.BoolVar(&f.binaryDiffs, "binaryDiffs", false, "Whether to encode diffs as base64.") - - return f -} - -func validateExecutorModeFlags(f *executorModeFlags) error { - if f.file == "" { - return errors.New("input file parameter missing") - } - if f.tempDir == "" { - return errors.New("tempDir parameter missing") - } - if f.repoDir == "" { - return errors.New("repoDir parameter missing") - } - - return nil -} - -func init() { - usage := ` -INTERNAL USE ONLY: 'src batch exec' executes the given raw batch spec in the given workspaces. - -The input file contains a JSON dump of the WorkspacesExecutionInput struct in -github.com/sourcegraph/sourcegraph/lib/batches. - -Usage: - - src batch exec -f FILE -repo DIR -workspaceFiles DIR [command options] - -Examples: - - $ src batch exec -f batch-spec-with-workspaces.json - -` - - flagSet := flag.NewFlagSet("exec", flag.ExitOnError) - flags := newExecutorModeFlags(flagSet) - - handler := func(args []string) error { - if err := flagSet.Parse(args); err != nil { - return err - } - - if len(flagSet.Args()) != 0 { - return cmderrors.Usage("additional arguments not allowed") - } - - if err := validateExecutorModeFlags(flags); err != nil { - return cmderrors.ExitCode(1, err) - } - - ctx, cancel := contextCancelOnInterrupt(context.Background()) - defer cancel() - - err := executeBatchSpecInWorkspaces(ctx, flags) - if err != nil { - return cmderrors.ExitCode(1, err) - } - - return nil - } - - batchCommands = append(batchCommands, &command{ - flagSet: flagSet, - handler: handler, - usageFunc: func() { - fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src batch %s':\n", flagSet.Name()) - flagSet.PrintDefaults() - fmt.Println(usage) - }, - }) -} - -func executeBatchSpecInWorkspaces(ctx context.Context, flags *executorModeFlags) (err error) { - ui := &ui.JSONLines{BinaryDiffs: flags.binaryDiffs} - - // Ensure the temp dir exists. - tempDir := flags.tempDir - if !filepath.IsAbs(tempDir) { - tempDir, err = filepath.Abs(tempDir) - if err != nil { - return errors.Wrap(err, "getting absolute path for temp dir") - } - - if err := os.Mkdir(tempDir, os.ModePerm); err != nil { - return errors.Wrap(err, "creating temp directory") - } - } - - // Grab the absolute path to the repo contents. - repoDir := flags.repoDir - if !filepath.IsAbs(repoDir) { - repoDir, err = filepath.Abs(repoDir) - if err != nil { - return errors.Wrap(err, "getting absolute path for repo dir") - } - } - - // Grab the absolute path to the workspace files contents. - workspaceFilesDir, err := filepath.Abs(flags.workspaceFilesDir) - if err != nil { - return errors.Wrap(err, "getting absolute path for workspace files dir") - } - - // Test if git is available. - if err := checkExecutable("git", "version"); err != nil { - return err - } - // Test if docker is available. - if err := checkExecutable("docker", "version"); err != nil { - return err - } - - // Read the input file that contains the raw spec and the workspaces in - // which to execute it. - input, err := loadWorkspaceExecutionInput(flags.file) - if err != nil { - return err - } - task := convertWorkspace(input) - - if len(task.Steps) == 0 { - return errors.New("invalid execution, no steps to process") - } - - imageCache := docker.NewImageCache() - - ui.PreparingContainerImages() - _, err = service.New(&service.Opts{}).EnsureDockerImages( - ctx, - imageCache, - task.Steps, - execPullParallelism, - ui.PreparingContainerImagesProgress, - ) - if err != nil { - return err - } - ui.PreparingContainerImagesSuccess() - - // Set up the execution UI. - taskExecUI := ui.ExecutingTasks(false, 1) - taskExecUI.Start([]*executor.Task{task}) - taskExecUI.TaskStarted(task) - - // Pass the os.Environ to run steps to allow access to the secrets set - // in the executor environment. - // The executor runtime takes care of not forwarding any sensitive secrets - // from the host, so this is safe. - globalEnv := os.Environ() - - opts := &executor.RunStepsOpts{ - Logger: &log.NoopTaskLogger{}, - WC: workspace.NewExecutorWorkspaceCreator(tempDir, repoDir), - EnsureImage: imageCache.Ensure, - Task: task, - // TODO: Should be slightly less than the executor timeout. Can we somehow read that? - Timeout: flags.timeout, - TempDir: tempDir, - WorkingDirectory: workspaceFilesDir, - GlobalEnv: globalEnv, - RepoArchive: &repozip.NoopArchive{}, - UI: taskExecUI.StepsExecutionUI(task), - ForceRoot: !flags.runAsImageUser, - BinaryDiffs: flags.binaryDiffs, - } - results, err := executor.RunSteps(ctx, opts) - - // Write all step cache results for all results. - for _, stepRes := range results { - cacheKey := task.CacheKey(globalEnv, workspaceFilesDir, stepRes.StepIndex) - k, err := cacheKey.Key() - if err != nil { - return errors.Wrap(err, "calculating step cache key") - } - ui.WriteAfterStepResult(k, stepRes) - } - - taskExecUI.TaskFinished(task, err) - - return err -} - -func loadWorkspaceExecutionInput(file string) (input batcheslib.WorkspacesExecutionInput, err error) { - f, err := batchOpenFileFlag(file) - if err != nil { - return input, err - } - defer f.Close() - - data, err := io.ReadAll(f) - if err != nil { - return input, errors.Wrap(err, "reading workspace execution input file") - } - - if err := json.Unmarshal(data, &input); err != nil { - return input, errors.Wrap(err, "unmarshaling workspace execution input file") - } - - return input, nil -} - -// convertWorkspace takes the WorkspacesExecutionInput and restructures it into -// an executor.Task. -func convertWorkspace(w batcheslib.WorkspacesExecutionInput) *executor.Task { - fileMatches := make(map[string]bool) - for _, path := range w.SearchResultPaths { - fileMatches[path] = true - } - - task := &executor.Task{ - Repository: &graphql.Repository{ - ID: w.Repository.ID, - Name: w.Repository.Name, - Branch: graphql.Branch{ - Name: w.Branch.Name, - Target: graphql.Target{ - OID: w.Branch.Target.OID, - }, - }, - Commit: graphql.Target{OID: w.Branch.Target.OID}, - FileMatches: fileMatches, - }, - Path: w.Path, - Steps: w.Steps, - OnlyFetchWorkspace: w.OnlyFetchWorkspace, - BatchChangeAttributes: &w.BatchChangeAttributes, - CachedStepResultFound: w.CachedStepResultFound, - CachedStepResult: w.CachedStepResult, - } - - return task -} diff --git a/cmd/src/doc_test.go b/cmd/src/doc_test.go index f99fea6e18..64d30ff1a7 100644 --- a/cmd/src/doc_test.go +++ b/cmd/src/doc_test.go @@ -33,7 +33,6 @@ var expectedDocFiles = []string{ "auth/index.md", "auth/token.md", "batch/apply.md", - "batch/exec.md", "batch/index.md", "batch/new.md", "batch/preview.md", diff --git a/internal/batches/workspace/executor_workspace.go b/internal/batches/workspace/executor_workspace.go deleted file mode 100644 index f6c4366755..0000000000 --- a/internal/batches/workspace/executor_workspace.go +++ /dev/null @@ -1,55 +0,0 @@ -package workspace - -import ( - "context" - - batcheslib "github.com/sourcegraph/sourcegraph/lib/batches" - - "github.com/sourcegraph/src-cli/internal/batches/graphql" - "github.com/sourcegraph/src-cli/internal/batches/repozip" -) - -func NewExecutorWorkspaceCreator(tempDir, repoDir string) Creator { - return &executorWorkspaceCreator{ - TempDir: tempDir, - RepoDir: repoDir, - } -} - -type executorWorkspaceCreator struct { - TempDir string - RepoDir string -} - -var _ Creator = &executorWorkspaceCreator{} - -func (wc *executorWorkspaceCreator) Create(ctx context.Context, repo *graphql.Repository, steps []batcheslib.Step, archive repozip.Archive) (Workspace, error) { - gitMetadata, err := snapshotGitMetadata(wc.RepoDir) - if err != nil { - return nil, err - } - - return &dockerBindExecutorWorkspace{ - dockerBindWorkspace: dockerBindWorkspace{ - tempDir: wc.TempDir, - dir: wc.RepoDir, - gitMetadata: gitMetadata, - }, - }, nil -} - -// dockerBindExecutorWorkspace implements a workspace that operates on the host FS -// and is mounted into the docker containers using a bind mount in the end. -// It is based on the dockerBindWorkspace implementation, but does no cleanup -// as that's handled by the executor, and we want to honor it's `keepWorkspaces` -// setting. -type dockerBindExecutorWorkspace struct { - dockerBindWorkspace -} - -var _ Workspace = &dockerBindExecutorWorkspace{} - -func (w *dockerBindExecutorWorkspace) Close(ctx context.Context) error { - // Nothing to do here, executor cleanup will handle this. - return nil -} diff --git a/lib/batches/workspaces_execution_input.go b/lib/batches/workspaces_execution_input.go deleted file mode 100644 index 76a5a8cb18..0000000000 --- a/lib/batches/workspaces_execution_input.go +++ /dev/null @@ -1,39 +0,0 @@ -package batches - -import ( - "github.com/sourcegraph/sourcegraph/lib/batches/execution" - "github.com/sourcegraph/sourcegraph/lib/batches/template" -) - -type WorkspacesExecutionInput struct { - BatchChangeAttributes template.BatchChangeAttributes - Repository WorkspaceRepo `json:"repository"` - Branch WorkspaceBranch `json:"branch"` - Path string `json:"path"` - OnlyFetchWorkspace bool `json:"onlyFetchWorkspace"` - Steps []Step `json:"steps"` - SearchResultPaths []string `json:"searchResultPaths"` - // CachedStepResultFound is only required for V1 executions. - // TODO: Remove me once V2 is the only execution format. - CachedStepResultFound bool `json:"cachedStepResultFound"` - // CachedStepResult is only required for V1 executions. - // TODO: Remove me once V2 is the only execution format. - CachedStepResult execution.AfterStepResult `json:"cachedStepResult"` - // SkippedSteps determines which steps are skipped in the execution. - SkippedSteps map[int]struct{} `json:"skippedSteps"` -} - -type WorkspaceRepo struct { - // ID is the GraphQL ID of the repository. - ID string `json:"id"` - Name string `json:"name"` -} - -type WorkspaceBranch struct { - Name string `json:"name"` - Target Commit `json:"target"` -} - -type Commit struct { - OID string `json:"oid"` -}