~edwargix/git.sr.ht

c06bc64d778555ef1bdbfeaefa0a17a53ae01c7b — Adnan Maolood 2 years ago 498c7c6
api/graph: Add cloneUrl to createRepository

Add support for cloning repositories by URL using the optional cloneUrl
parameter.
M api/graph/api/generated.go => api/graph/api/generated.go +18 -6
@@ 116,7 116,7 @@ type ComplexityRoot struct {
	}

	Mutation struct {
		CreateRepository func(childComplexity int, name string, visibility model.Visibility, description *string) int
		CreateRepository func(childComplexity int, name string, visibility model.Visibility, description *string, cloneURL *string) int
		CreateWebhook    func(childComplexity int, config model.UserWebhookInput) int
		DeleteACL        func(childComplexity int, id int) int
		DeleteArtifact   func(childComplexity int, id int) int


@@ 301,7 301,7 @@ type CommitResolver interface {
	Diff(ctx context.Context, obj *model.Commit) (string, error)
}
type MutationResolver interface {
	CreateRepository(ctx context.Context, name string, visibility model.Visibility, description *string) (*model.Repository, error)
	CreateRepository(ctx context.Context, name string, visibility model.Visibility, description *string, cloneURL *string) (*model.Repository, error)
	UpdateRepository(ctx context.Context, id int, input map[string]interface{}) (*model.Repository, error)
	DeleteRepository(ctx context.Context, id int) (*model.Repository, error)
	UpdateACL(ctx context.Context, repoID int, mode model.AccessMode, entity string) (*model.ACL, error)


@@ 607,7 607,7 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in
			return 0, false
		}

		return e.complexity.Mutation.CreateRepository(childComplexity, args["name"].(string), args["visibility"].(model.Visibility), args["description"].(*string)), true
		return e.complexity.Mutation.CreateRepository(childComplexity, args["name"].(string), args["visibility"].(model.Visibility), args["description"].(*string), args["cloneUrl"].(*string)), true

	case "Mutation.createWebhook":
		if e.complexity.Mutation.CreateWebhook == nil {


@@ 2097,8 2097,11 @@ input UserWebhookInput {
}

type Mutation {
  "Creates a new git repository"
  createRepository(name: String!, visibility: Visibility!, description: String): Repository @access(scope: REPOSITORIES, kind: RW)
  """
  Creates a new git repository. If the cloneUrl parameter is specified, the
  repository will be cloned from the given URL.
  """
  createRepository(name: String!, visibility: Visibility!, description: String, cloneUrl: String): Repository @access(scope: REPOSITORIES, kind: RW)

  "Updates the metadata for a git repository"
  updateRepository(id: Int!, input: RepoInput!): Repository @access(scope: REPOSITORIES, kind: RW)


@@ 2221,6 2224,15 @@ func (ec *executionContext) field_Mutation_createRepository_args(ctx context.Con
		}
	}
	args["description"] = arg2
	var arg3 *string
	if tmp, ok := rawArgs["cloneUrl"]; ok {
		ctx := graphql.WithPathContext(ctx, graphql.NewPathWithField("cloneUrl"))
		arg3, err = ec.unmarshalOString2ᚖstring(ctx, tmp)
		if err != nil {
			return nil, err
		}
	}
	args["cloneUrl"] = arg3
	return args, nil
}



@@ 3954,7 3966,7 @@ func (ec *executionContext) _Mutation_createRepository(ctx context.Context, fiel
	resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) {
		directive0 := func(rctx context.Context) (interface{}, error) {
			ctx = rctx // use context from middleware stack in children
			return ec.resolvers.Mutation().CreateRepository(rctx, args["name"].(string), args["visibility"].(model.Visibility), args["description"].(*string))
			return ec.resolvers.Mutation().CreateRepository(rctx, args["name"].(string), args["visibility"].(model.Visibility), args["description"].(*string), args["cloneUrl"].(*string))
		}
		directive1 := func(ctx context.Context) (interface{}, error) {
			scope, err := ec.unmarshalNAccessScope2gitᚗsrᚗhtᚋאsircmpwnᚋgitᚗsrᚗhtᚋapiᚋgraphᚋmodelᚐAccessScope(ctx, "REPOSITORIES")

M api/graph/resolver.go => api/graph/resolver.go +22 -0
@@ 1,7 1,12 @@
package graph

import (
	"context"
	"fmt"
	"regexp"

	"github.com/99designs/gqlgen/graphql"
	"github.com/vektah/gqlparser/v2/gqlerror"
)

type Resolver struct{}


@@ 9,3 14,20 @@ type Resolver struct{}
var (
	repoNameRE = regexp.MustCompile(`^[A-Za-z0-9._-]+$`)
)

func gqlErrorf(ctx context.Context, field string, message string, items ...interface{}) *gqlerror.Error {
	err := &gqlerror.Error{
		Message: fmt.Sprintf(message, items...),
		Path:    graphql.GetPath(ctx),
		Extensions: map[string]interface{}{
			"field": field,
		},
	}
	return err
}

var allowedCloneSchemes = map[string]struct{}{
	"https": struct{}{},
	"http":  struct{}{},
	"git":   struct{}{},
}

M api/graph/schema.graphqls => api/graph/schema.graphqls +5 -2
@@ 522,8 522,11 @@ input UserWebhookInput {
}

type Mutation {
  "Creates a new git repository"
  createRepository(name: String!, visibility: Visibility!, description: String): Repository @access(scope: REPOSITORIES, kind: RW)
  """
  Creates a new git repository. If the cloneUrl parameter is specified, the
  repository will be cloned from the given URL.
  """
  createRepository(name: String!, visibility: Visibility!, description: String, cloneUrl: String): Repository @access(scope: REPOSITORIES, kind: RW)

  "Updates the metadata for a git repository"
  updateRepository(id: Int!, input: RepoInput!): Repository @access(scope: REPOSITORIES, kind: RW)

M api/graph/schema.resolvers.go => api/graph/schema.resolvers.go +57 -5
@@ 71,7 71,7 @@ func (r *commitResolver) Diff(ctx context.Context, obj *model.Commit) (string, e
	return obj.DiffContext(ctx), nil
}

func (r *mutationResolver) CreateRepository(ctx context.Context, name string, visibility model.Visibility, description *string) (*model.Repository, error) {
func (r *mutationResolver) CreateRepository(ctx context.Context, name string, visibility model.Visibility, description *string, cloneURL *string) (*model.Repository, error) {
	if !repoNameRE.MatchString(name) {
		return nil, fmt.Errorf("Invalid repository name '%s' (must match %s)",
			name, repoNameRE.String())


@@ 141,15 141,67 @@ func (r *mutationResolver) CreateRepository(ctx context.Context, name string, vi
			&repo.Description, &repo.RawVisibility, &repo.UpstreamURL,
			&repo.Path, &repo.OwnerID); err != nil {
			if strings.Contains(err.Error(), "duplicate key value violates unique constraint") {
				return fmt.Errorf("A repository with this name already exists.")
				return gqlErrorf(ctx, "name", "A repository with this name already exists.")
			}
			return err
		}

		gitrepo, err := git.PlainInit(repoPath, true)
		if err != nil {
			return err
		var gitrepo *git.Repository
		if cloneURL != nil {
			u, err := url.Parse(*cloneURL)
			if err != nil {
				return err
			} else if u.Host == "" {
				return gqlErrorf(ctx, "cloneUrl", "Cannot use URL without host")
			} else if _, ok := allowedCloneSchemes[u.Scheme]; !ok {
				return gqlErrorf(ctx, "cloneUrl", "Unsupported protocol %q", u.Scheme)
			}

			origin := config.GetOrigin(conf, "git.sr.ht", true)
			o, err := url.Parse(origin)
			if err != nil {
				panic(err)
			}

			// Check if this is a local repository
			if u.Scheme == o.Scheme && u.Host == o.Host {
				u.Path = strings.TrimPrefix(u.Path, "/")
				split := strings.SplitN(u.Path, "/", 2)
				if len(split) != 2 {
					return gqlErrorf(ctx, "cloneUrl", "Invalid clone URL")
				}
				canonicalName, repoName := split[0], split[1]
				entity := canonicalName
				if strings.HasPrefix(entity, "~") {
					entity = entity[1:]
				} else {
					return gqlErrorf(ctx, "cloneUrl", "Invalid username")
				}
				repo, err := loaders.ForContext(ctx).
					RepositoriesByOwnerRepoName.Load([2]string{entity, repoName})
				if err != nil {
					return err
				} else if repo == nil {
					return gqlErrorf(ctx, "cloneUrl", "Repository not found")
				}
				cloneURL = &repo.Path
			}

			gitrepo, err = git.PlainClone(repoPath, true, &git.CloneOptions{
				URL:               *cloneURL,
				RecurseSubmodules: git.NoRecurseSubmodules,
			})
			if err != nil {
				return gqlErrorf(ctx, "cloneUrl", "%s", err)
			}
		} else {
			var err error
			gitrepo, err = git.PlainInit(repoPath, true)
			if err != nil {
				return err
			}
		}

		repoCreated = true
		config, err := gitrepo.Config()
		if err != nil {