diff --git a/platform/extension/gitworkspace/BUILD.bazel b/platform/extension/gitworkspace/BUILD.bazel new file mode 100644 index 00000000..c68827bd --- /dev/null +++ b/platform/extension/gitworkspace/BUILD.bazel @@ -0,0 +1,8 @@ +load("@rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["gitworkspace.go"], + importpath = "github.com/uber/submitqueue/platform/extension/gitworkspace", + visibility = ["//visibility:public"], +) diff --git a/platform/extension/gitworkspace/gitworkspace.go b/platform/extension/gitworkspace/gitworkspace.go new file mode 100644 index 00000000..c9c22dfd --- /dev/null +++ b/platform/extension/gitworkspace/gitworkspace.go @@ -0,0 +1,64 @@ +// Copyright (c) 2026 Uber Technologies, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package gitworkspace defines the pluggable interface for executing git +// commands in a stateful workspace. The OSS implementation runs commands as +// local subprocesses; alternative deployments wrap their backend's exec API +// in the same contract. +package gitworkspace + +//go:generate mockgen -source=gitworkspace.go -destination=mock/gitworkspace_mock.go -package=mock + +import "context" + +// Command is one command to execute in a git workspace. +type Command struct { + // Alias identifies this command's Output in the response. + Alias string + // Bin is the executable name. + Bin string + // Args are the command-line arguments. + Args []string + // Stdin is optional standard input. + Stdin string +} + +// Output is the result of one executed command. +type Output struct { + // Alias matches the Command that produced this output. + Alias string + // ExitCode is the process exit code (0 = success, -1 = skipped). + ExitCode int32 + // Stdout is the captured standard output. + Stdout string + // Stderr is the captured standard error. + Stderr string +} + +// Workspace is a stateful git environment where command batches execute +// sequentially. Commands within one Exec call run in order; a non-zero +// exit code skips the remaining commands in that batch. State persists +// across Exec calls on the same Workspace. +type Workspace interface { + // Exec sends a batch of commands and returns their outputs. + Exec(commands []Command) ([]Output, error) + // Close releases the workspace and its resources. + Close() error +} + +// Factory creates Workspace instances bound to a repository. +type Factory interface { + // For returns a Workspace for the given repository. + For(ctx context.Context, repo string) (Workspace, error) +} diff --git a/platform/extension/gitworkspace/mock/BUILD.bazel b/platform/extension/gitworkspace/mock/BUILD.bazel new file mode 100644 index 00000000..5d768ea4 --- /dev/null +++ b/platform/extension/gitworkspace/mock/BUILD.bazel @@ -0,0 +1,12 @@ +load("@rules_go//go:def.bzl", "go_library") + +go_library( + name = "go_default_library", + srcs = ["gitworkspace_mock.go"], + importpath = "github.com/uber/submitqueue/platform/extension/gitworkspace/mock", + visibility = ["//visibility:public"], + deps = [ + "//platform/extension/gitworkspace:go_default_library", + "@org_uber_go_mock//gomock:go_default_library", + ], +) diff --git a/platform/extension/gitworkspace/mock/gitworkspace_mock.go b/platform/extension/gitworkspace/mock/gitworkspace_mock.go new file mode 100644 index 00000000..c2617943 --- /dev/null +++ b/platform/extension/gitworkspace/mock/gitworkspace_mock.go @@ -0,0 +1,103 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: gitworkspace.go + +// Package mock is a generated GoMock package. +package mock + +import ( + context "context" + reflect "reflect" + + gitworkspace "github.com/uber/submitqueue/platform/extension/gitworkspace" + gomock "go.uber.org/mock/gomock" +) + +// MockWorkspace is a mock of Workspace interface. +type MockWorkspace struct { + ctrl *gomock.Controller + recorder *MockWorkspaceMockRecorder +} + +// MockWorkspaceMockRecorder is the mock recorder for MockWorkspace. +type MockWorkspaceMockRecorder struct { + mock *MockWorkspace +} + +// NewMockWorkspace creates a new mock instance. +func NewMockWorkspace(ctrl *gomock.Controller) *MockWorkspace { + mock := &MockWorkspace{ctrl: ctrl} + mock.recorder = &MockWorkspaceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockWorkspace) EXPECT() *MockWorkspaceMockRecorder { + return m.recorder +} + +// Close mocks base method. +func (m *MockWorkspace) Close() error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Close") + ret0, _ := ret[0].(error) + return ret0 +} + +// Close indicates an expected call of Close. +func (mr *MockWorkspaceMockRecorder) Close() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockWorkspace)(nil).Close)) +} + +// Exec mocks base method. +func (m *MockWorkspace) Exec(commands []gitworkspace.Command) ([]gitworkspace.Output, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Exec", commands) + ret0, _ := ret[0].([]gitworkspace.Output) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Exec indicates an expected call of Exec. +func (mr *MockWorkspaceMockRecorder) Exec(commands interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockWorkspace)(nil).Exec), commands) +} + +// MockFactory is a mock of Factory interface. +type MockFactory struct { + ctrl *gomock.Controller + recorder *MockFactoryMockRecorder +} + +// MockFactoryMockRecorder is the mock recorder for MockFactory. +type MockFactoryMockRecorder struct { + mock *MockFactory +} + +// NewMockFactory creates a new mock instance. +func NewMockFactory(ctrl *gomock.Controller) *MockFactory { + mock := &MockFactory{ctrl: ctrl} + mock.recorder = &MockFactoryMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockFactory) EXPECT() *MockFactoryMockRecorder { + return m.recorder +} + +// For mocks base method. +func (m *MockFactory) For(ctx context.Context, repo string) (gitworkspace.Workspace, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "For", ctx, repo) + ret0, _ := ret[0].(gitworkspace.Workspace) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// For indicates an expected call of For. +func (mr *MockFactoryMockRecorder) For(ctx, repo interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "For", reflect.TypeOf((*MockFactory)(nil).For), ctx, repo) +}