mirror of https://github.com/harness/drone.git
feat: [CDE-137]: check valid code repository for CDE use. (#2197)
* feat: [CDE-137]: check valid code repository for CDE use. * feat: [CDE-137]: check valid code repository for CDE use. * feat: [CDE-137]: check valid code repository for CDE use.unified-ui
parent
76682ad400
commit
18226c8914
|
@ -29,10 +29,12 @@ import (
|
|||
"github.com/harness/gitness/types/enum"
|
||||
|
||||
gonanoid "github.com/matoous/go-nanoid"
|
||||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
const defaultAccessKey = "Harness@123"
|
||||
const defaultMachineUser = "harness"
|
||||
const gitspaceTimedOutInMintues = 5
|
||||
|
||||
type ActionInput struct {
|
||||
Action enum.GitspaceActionType `json:"action"`
|
||||
|
@ -115,7 +117,10 @@ func (c *Controller) startGitspaceAction(
|
|||
config.State, _ = enum.GetGitspaceStateFromInstance(newGitspaceInstance.State)
|
||||
ctx2 := context.WithoutCancel(ctx)
|
||||
go func() {
|
||||
_, _ = c.startAsyncOperation(ctx2, config)
|
||||
err := c.startAsyncOperation(ctx2, config)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("start operation failed")
|
||||
}
|
||||
}()
|
||||
return config, nil
|
||||
}
|
||||
|
@ -123,17 +128,15 @@ func (c *Controller) startGitspaceAction(
|
|||
func (c *Controller) startAsyncOperation(
|
||||
ctx context.Context,
|
||||
config *types.GitspaceConfig,
|
||||
) (*types.GitspaceConfig, error) {
|
||||
) error {
|
||||
updatedGitspace, orchestrateErr := c.orchestrator.StartGitspace(ctx, config)
|
||||
if err := c.gitspaceInstanceStore.Update(ctx, updatedGitspace); err != nil {
|
||||
return nil, fmt.Errorf("failed to update gitspace %w %w", err, orchestrateErr)
|
||||
return fmt.Errorf("failed to update gitspace %w %w", err, orchestrateErr)
|
||||
}
|
||||
if orchestrateErr != nil {
|
||||
return nil, fmt.Errorf("failed to find start gitspace : %s %w", config.Identifier, orchestrateErr)
|
||||
return fmt.Errorf("failed to find start gitspace : %s %w", config.Identifier, orchestrateErr)
|
||||
}
|
||||
config.GitspaceInstance = updatedGitspace
|
||||
config.State, _ = enum.GetGitspaceStateFromInstance(updatedGitspace.State)
|
||||
return config, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) createGitspaceInstance(config *types.GitspaceConfig) (*types.GitspaceInstance, error) {
|
||||
|
@ -171,9 +174,8 @@ func (c *Controller) gitspaceBusyOperation(
|
|||
if config.GitspaceInstance == nil {
|
||||
return config, nil
|
||||
}
|
||||
const timedOutInSeconds = 5
|
||||
if config.GitspaceInstance.State.IsBusyStatus() &&
|
||||
time.Since(time.UnixMilli(config.GitspaceInstance.Updated)).Milliseconds() <= (timedOutInSeconds*60*1000) {
|
||||
time.Since(time.UnixMilli(config.GitspaceInstance.Updated)).Milliseconds() <= (gitspaceTimedOutInMintues*60*1000) {
|
||||
return nil, fmt.Errorf("gitspace start/stop is already pending for : %q", config.Identifier)
|
||||
} else if config.GitspaceInstance.State.IsBusyStatus() {
|
||||
config.GitspaceInstance.State = enum.GitspaceInstanceStateError
|
||||
|
@ -208,7 +210,10 @@ func (c *Controller) stopGitspaceAction(
|
|||
config.State, _ = enum.GetGitspaceStateFromInstance(savedGitspace.State)
|
||||
ctx2 := context.WithoutCancel(ctx)
|
||||
go func() {
|
||||
_, _ = c.stopAsyncOperation(ctx2, config)
|
||||
err := c.stopAsyncOperation(ctx2, config)
|
||||
if err != nil {
|
||||
log.Err(err).Msg("stop operation failed")
|
||||
}
|
||||
}()
|
||||
return config, err
|
||||
}
|
||||
|
@ -216,25 +221,23 @@ func (c *Controller) stopGitspaceAction(
|
|||
func (c *Controller) stopAsyncOperation(
|
||||
ctx context.Context,
|
||||
config *types.GitspaceConfig,
|
||||
) (*types.GitspaceConfig, error) {
|
||||
) error {
|
||||
savedGitspace := config.GitspaceInstance
|
||||
updatedGitspace, orchestrateErr := c.orchestrator.StopGitspace(ctx, config)
|
||||
if updatedGitspace != nil {
|
||||
if err := c.gitspaceInstanceStore.Update(ctx, updatedGitspace); err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
return fmt.Errorf(
|
||||
"unable to update the gitspace with config id %s %w %w",
|
||||
savedGitspace.Identifier,
|
||||
err,
|
||||
orchestrateErr)
|
||||
}
|
||||
if orchestrateErr != nil {
|
||||
return nil, fmt.Errorf(
|
||||
return fmt.Errorf(
|
||||
"failed to stop gitspace instance with ID %s %w", savedGitspace.Identifier, orchestrateErr)
|
||||
}
|
||||
}
|
||||
config.GitspaceInstance = updatedGitspace
|
||||
config.State, _ = enum.GetGitspaceStateFromInstance(updatedGitspace.State)
|
||||
return config, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Controller) sanitizeActionInput(in *ActionInput) error {
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
gitspaceevents "github.com/harness/gitness/app/events/gitspace"
|
||||
"github.com/harness/gitness/app/gitspace/logutil"
|
||||
"github.com/harness/gitness/app/gitspace/orchestrator"
|
||||
"github.com/harness/gitness/app/gitspace/scm"
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
)
|
||||
|
@ -34,6 +35,7 @@ type Controller struct {
|
|||
gitspaceEventStore store.GitspaceEventStore
|
||||
tx dbtx.Transactor
|
||||
statefulLogger *logutil.StatefulLogger
|
||||
scm scm.SCM
|
||||
}
|
||||
|
||||
func NewController(
|
||||
|
@ -47,6 +49,7 @@ func NewController(
|
|||
orchestrator orchestrator.Orchestrator,
|
||||
gitspaceEventStore store.GitspaceEventStore,
|
||||
statefulLogger *logutil.StatefulLogger,
|
||||
scm scm.SCM,
|
||||
) *Controller {
|
||||
return &Controller{
|
||||
tx: tx,
|
||||
|
@ -59,5 +62,6 @@ func NewController(
|
|||
orchestrator: orchestrator,
|
||||
gitspaceEventStore: gitspaceEventStore,
|
||||
statefulLogger: statefulLogger,
|
||||
scm: scm,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2023 Harness, 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 gitspace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
apiauth "github.com/harness/gitness/app/api/auth"
|
||||
"github.com/harness/gitness/app/auth"
|
||||
"github.com/harness/gitness/app/gitspace/scm"
|
||||
"github.com/harness/gitness/types/enum"
|
||||
)
|
||||
|
||||
type LookupRepoInput struct {
|
||||
Identifier string `json:"-"`
|
||||
SpaceRef string `json:"space_ref"` // Ref of the parent space
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
func (c *Controller) LookupRepo(
|
||||
ctx context.Context,
|
||||
session *auth.Session,
|
||||
in *LookupRepoInput,
|
||||
) (*scm.CodeRepositoryResponse, error) {
|
||||
space, err := c.spaceStore.FindByRef(ctx, in.SpaceRef)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find space: %w", err)
|
||||
}
|
||||
err = apiauth.CheckGitspace(ctx, c.authorizer, session, space.Path, "", enum.PermissionGitspaceEdit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to authorize: %w", err)
|
||||
}
|
||||
repositoryRequest := scm.CodeRepositoryRequest{URL: in.URL}
|
||||
codeRepositoryResponse, err := c.scm.CheckValidCodeRepo(ctx, repositoryRequest)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return codeRepositoryResponse, nil
|
||||
}
|
|
@ -19,6 +19,7 @@ import (
|
|||
gitspaceevents "github.com/harness/gitness/app/events/gitspace"
|
||||
"github.com/harness/gitness/app/gitspace/logutil"
|
||||
"github.com/harness/gitness/app/gitspace/orchestrator"
|
||||
"github.com/harness/gitness/app/gitspace/scm"
|
||||
"github.com/harness/gitness/app/store"
|
||||
"github.com/harness/gitness/store/database/dbtx"
|
||||
|
||||
|
@ -41,6 +42,7 @@ func ProvideController(
|
|||
orchestrator orchestrator.Orchestrator,
|
||||
eventStore store.GitspaceEventStore,
|
||||
statefulLogger *logutil.StatefulLogger,
|
||||
scm scm.SCM,
|
||||
) *Controller {
|
||||
return NewController(
|
||||
tx,
|
||||
|
@ -53,5 +55,6 @@ func ProvideController(
|
|||
orchestrator,
|
||||
eventStore,
|
||||
statefulLogger,
|
||||
scm,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright 2023 Harness, 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 gitspace
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
|
||||
"github.com/harness/gitness/app/api/controller/gitspace"
|
||||
"github.com/harness/gitness/app/api/render"
|
||||
"github.com/harness/gitness/app/api/request"
|
||||
)
|
||||
|
||||
func HandleLookupRepo(gitspaceCtrl *gitspace.Controller) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
session, _ := request.AuthSessionFrom(ctx)
|
||||
in := new(gitspace.LookupRepoInput)
|
||||
err := json.NewDecoder(r.Body).Decode(in)
|
||||
if err != nil {
|
||||
render.BadRequestf(ctx, w, "Invalid Request Body: %s.", err)
|
||||
return
|
||||
}
|
||||
repositoryResponse, err := gitspaceCtrl.LookupRepo(ctx, session, in)
|
||||
if err != nil {
|
||||
render.TranslatedUserError(ctx, w, err)
|
||||
return
|
||||
}
|
||||
render.JSON(w, http.StatusOK, repositoryResponse)
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import (
|
|||
|
||||
"github.com/harness/gitness/app/api/controller/gitspace"
|
||||
"github.com/harness/gitness/app/api/usererror"
|
||||
"github.com/harness/gitness/app/gitspace/scm"
|
||||
"github.com/harness/gitness/livelog"
|
||||
"github.com/harness/gitness/types"
|
||||
|
||||
|
@ -29,6 +30,10 @@ type createGitspaceRequest struct {
|
|||
gitspace.CreateInput
|
||||
}
|
||||
|
||||
type lookupRepoGitspaceRequest struct {
|
||||
gitspace.LookupRepoInput
|
||||
}
|
||||
|
||||
type updateGitspaceRequest struct {
|
||||
}
|
||||
|
||||
|
@ -138,4 +143,16 @@ func gitspaceOperations(reflector *openapi3.Reflector) {
|
|||
_ = reflector.SetJSONResponse(&opStreamLogs, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opStreamLogs, new(usererror.Error), http.StatusNotFound)
|
||||
_ = reflector.Spec.AddOperation(http.MethodGet, "/gitspaces/{gitspace_identifier}/logs/stream", opStreamLogs)
|
||||
|
||||
opRepoLookup := openapi3.Operation{}
|
||||
opRepoLookup.WithTags("gitspaces")
|
||||
opRepoLookup.WithSummary("Validate git repo for gitspaces")
|
||||
opRepoLookup.WithMapOfAnything(map[string]interface{}{"operationId": "repoLookupForGitspace"})
|
||||
_ = reflector.SetRequest(&opCreate, new(lookupRepoGitspaceRequest), http.MethodPost)
|
||||
_ = reflector.SetJSONResponse(&opRepoLookup, new(scm.CodeRepositoryResponse), http.StatusCreated)
|
||||
_ = reflector.SetJSONResponse(&opRepoLookup, new(usererror.Error), http.StatusBadRequest)
|
||||
_ = reflector.SetJSONResponse(&opRepoLookup, new(usererror.Error), http.StatusInternalServerError)
|
||||
_ = reflector.SetJSONResponse(&opRepoLookup, new(usererror.Error), http.StatusUnauthorized)
|
||||
_ = reflector.SetJSONResponse(&opRepoLookup, new(usererror.Error), http.StatusForbidden)
|
||||
_ = reflector.Spec.AddOperation(http.MethodPost, "/gitspaces/lookup-repo", opCreate)
|
||||
}
|
||||
|
|
|
@ -70,11 +70,11 @@ func (o orchestrator) StartGitspace(
|
|||
if err != nil {
|
||||
o.emitGitspaceEvent(ctx, gitspaceConfig, enum.GitspaceEventTypeFetchDevcontainerFailed)
|
||||
|
||||
log.Warn().Err(err).Msg("devcontainerConfig fetch failed.")
|
||||
log.Warn().Err(err).Msg("devcontainer config fetch failed.")
|
||||
}
|
||||
|
||||
if devcontainerConfig == nil {
|
||||
log.Warn().Err(err).Msg("devcontainerConfig is nil, using empty config")
|
||||
log.Warn().Err(err).Msg("devcontainer config is nil, using empty config")
|
||||
devcontainerConfig = &types.DevcontainerConfig{}
|
||||
}
|
||||
|
||||
|
@ -82,7 +82,7 @@ func (o orchestrator) StartGitspace(
|
|||
|
||||
infraProviderResource, err := o.infraProviderResourceStore.Find(ctx, gitspaceConfig.InfraProviderResourceID)
|
||||
if err != nil {
|
||||
return gitspaceInstance, fmt.Errorf("cannot get the infraProviderResource for ID %d: %w",
|
||||
return gitspaceInstance, fmt.Errorf("cannot get the infraprovider resource for ID %d: %w",
|
||||
gitspaceConfig.InfraProviderResourceID, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// Copyright 2023 Harness, 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 scm
|
||||
|
||||
type CodeRepositoryRequest struct {
|
||||
URL string `json:"url"`
|
||||
}
|
||||
|
||||
type CodeRepositoryResponse struct {
|
||||
URL string `json:"url"`
|
||||
Branch string `json:"branch,omitempty"`
|
||||
CodeRepoIsPrivate bool `json:"is_private"`
|
||||
}
|
|
@ -18,6 +18,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
|
@ -32,6 +33,10 @@ import (
|
|||
"github.com/rs/zerolog/log"
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoDefaultBranch = errors.New("no default branch")
|
||||
)
|
||||
|
||||
var _ SCM = (*scm)(nil)
|
||||
|
||||
type SCM interface {
|
||||
|
@ -39,10 +44,37 @@ type SCM interface {
|
|||
DevcontainerConfig(ctx context.Context, gitspaceConfig *types.GitspaceConfig) (*types.DevcontainerConfig, error)
|
||||
// RepositoryName finds the repository name for the code repo URL from its provider.
|
||||
RepositoryName(ctx context.Context, gitspaceConfig *types.GitspaceConfig) (string, error)
|
||||
// check if the current URL is a valid and accessible code repo, input can be connector info, user token etc.
|
||||
CheckValidCodeRepo(ctx context.Context, request CodeRepositoryRequest,
|
||||
) (*CodeRepositoryResponse, error)
|
||||
}
|
||||
|
||||
type scm struct{}
|
||||
|
||||
func (s scm) CheckValidCodeRepo(ctx context.Context, request CodeRepositoryRequest) (*CodeRepositoryResponse, error) {
|
||||
err := validateURL(request)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid URL, %w", err)
|
||||
}
|
||||
codeRepositoryResponse := &CodeRepositoryResponse{
|
||||
URL: request.URL,
|
||||
CodeRepoIsPrivate: true,
|
||||
}
|
||||
defaultBranch, err := detectDefaultGitBranch(ctx, request.URL)
|
||||
if err == nil {
|
||||
branch := "main"
|
||||
if defaultBranch != "" {
|
||||
branch = defaultBranch
|
||||
}
|
||||
codeRepositoryResponse = &CodeRepositoryResponse{
|
||||
URL: request.URL,
|
||||
Branch: branch,
|
||||
CodeRepoIsPrivate: false,
|
||||
}
|
||||
}
|
||||
return codeRepositoryResponse, nil
|
||||
}
|
||||
|
||||
func NewSCM() SCM {
|
||||
return &scm{}
|
||||
}
|
||||
|
@ -158,6 +190,32 @@ func removeComments(input []byte) []byte {
|
|||
return lineCommentRegex.ReplaceAll(input, nil)
|
||||
}
|
||||
|
||||
func detectDefaultGitBranch(ctx context.Context, gitRepoDir string) (string, error) {
|
||||
cmd := command.New("ls-remote",
|
||||
command.WithFlag("--symref"),
|
||||
command.WithFlag("-q"),
|
||||
command.WithArg(gitRepoDir),
|
||||
command.WithArg("HEAD"),
|
||||
)
|
||||
output := &bytes.Buffer{}
|
||||
if err := cmd.Run(ctx, command.WithStdout(output)); err != nil {
|
||||
return "", fmt.Errorf("failed to ls remote repo")
|
||||
}
|
||||
var lsRemoteHeadRegexp = regexp.MustCompile(`ref: refs/heads/([^\s]+)\s+HEAD`)
|
||||
match := lsRemoteHeadRegexp.FindStringSubmatch(strings.TrimSpace(output.String()))
|
||||
if match == nil {
|
||||
return "", ErrNoDefaultBranch
|
||||
}
|
||||
return match[1], nil
|
||||
}
|
||||
|
||||
func validateURL(request CodeRepositoryRequest) error {
|
||||
if _, err := url.ParseRequestURI(request.URL); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateArgs(_ *types.GitspaceConfig) error {
|
||||
// TODO Validate the args
|
||||
return nil
|
||||
|
|
|
@ -709,6 +709,7 @@ func setupKeywordSearch(r chi.Router, searchCtrl *keywordsearch.Controller) {
|
|||
|
||||
func setupGitspaces(r chi.Router, gitspacesCtrl *gitspace.Controller) {
|
||||
r.Route("/gitspaces", func(r chi.Router) {
|
||||
r.Post("/lookup-repo", handlergitspace.HandleLookupRepo(gitspacesCtrl))
|
||||
r.Post("/", handlergitspace.HandleCreateConfig(gitspacesCtrl))
|
||||
r.Route(fmt.Sprintf("/{%s}", request.PathParamGitspaceIdentifier), func(r chi.Router) {
|
||||
r.Get("/", handlergitspace.HandleFind(gitspacesCtrl))
|
||||
|
|
|
@ -116,7 +116,6 @@ func (g gitspaceEventStore) List(
|
|||
queryStmt := database.Builder.
|
||||
Select(gitspaceEventsColumnsWithID).
|
||||
From(gitspaceEventsTable)
|
||||
|
||||
queryStmt = g.setQueryFilter(queryStmt, filter)
|
||||
|
||||
queryStmt = g.setSortFilter(queryStmt, filter)
|
||||
|
@ -177,7 +176,7 @@ func (g gitspaceEventStore) setSortFilter(
|
|||
stmt squirrel.SelectBuilder,
|
||||
_ *types.GitspaceEventFilter,
|
||||
) squirrel.SelectBuilder {
|
||||
return stmt.OrderBy("geven_created ASC")
|
||||
return stmt.OrderBy("geven_created DESC")
|
||||
}
|
||||
|
||||
func (g gitspaceEventStore) setPaginationFilter(
|
||||
|
|
|
@ -343,7 +343,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
|
|||
containerOrchestrator := container.ProvideEmbeddedDockerOrchestrator(dockerClientFactory, vsCode, vsCodeWeb, containerConfig, statefulLogger)
|
||||
orchestratorOrchestrator := orchestrator.ProvideOrchestrator(scmSCM, infraProviderResourceStore, infraProvisioner, containerOrchestrator, reporter3)
|
||||
gitspaceEventStore := database.ProvideGitspaceEventStore(db)
|
||||
gitspaceController := gitspace.ProvideController(transactor, authorizer, infraProviderResourceStore, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, reporter3, orchestratorOrchestrator, gitspaceEventStore, statefulLogger)
|
||||
gitspaceController := gitspace.ProvideController(transactor, authorizer, infraProviderResourceStore, gitspaceConfigStore, gitspaceInstanceStore, spaceStore, reporter3, orchestratorOrchestrator, gitspaceEventStore, statefulLogger, scmSCM)
|
||||
migrateController := migrate.ProvideController(authorizer, principalStore)
|
||||
apiHandler := router.ProvideAPIHandler(ctx, config, authenticator, repoController, reposettingsController, executionController, logsController, spaceController, pipelineController, secretController, triggerController, connectorController, templateController, pluginController, pullreqController, webhookController, githookController, gitInterface, serviceaccountController, controller, principalController, checkController, systemController, uploadController, keywordsearchController, infraproviderController, gitspaceController, migrateController)
|
||||
gitHandler := router.ProvideGitHandler(provider, authenticator, repoController)
|
||||
|
|
Loading…
Reference in New Issue