mirror of
https://gitea.com/gitea/tea.git
synced 2026-10-26 11:01:25 +01:00
feat(repo): validate create templates, publish local projects, verify edits
repo create --license NOPE returned Gitea's HTTP 500 internals. License, .gitignore and label templates are now checked against the server's lists, case-insensitively, and complete with Tab. --source DIR adds the new repository as a remote of a local Git repository and --push pushes its current branch; --public is the explicit opposite of --private, and -d/-g/-l and the new alias follow gh. --trust-model now offers the names Gitea accepts. A prompted name is confirmed with the visibility, owner and host, and creation reports "✓ Created repository ..." and the URL. repo edit --default-branch nope exited 0 without changing anything; the branch must now exist. Edit gains --visibility, --enable-issues/-wiki/ -pull-requests/-projects, --allow-merge-commit/-squash/-rebase, --delete-branch-on-merge, --default-merge-style and --add-topic/ --remove-topic, and reports which settings it changed. repo migrate names the repository after the source URL, validates --service before prompting, and reports the import with the URL. Signed-off-by: bircni <bircni@icloud.com>
This commit is contained in:
9 files changed
+742
-308
No files matched your search
+266
-126
@@ -5,12 +5,16 @@ package app
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"gitea.dev/tea/internal/clierr"
|
||||
"gitea.dev/tea/internal/services"
|
||||
"gitea.dev/tea/internal/terminal"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -24,147 +28,283 @@ func validateRepositoryName(name string) error {
|
||||
func (a *application) repoCreateCommand() *cobra.Command {
|
||||
var opts services.RepositoryOptions
|
||||
var templateOpts services.RepositoryTemplateOptions
|
||||
var org, source, team string
|
||||
var clone bool
|
||||
command := &cobra.Command{Use: "create [name]", Short: "Create a repository, optionally from a template", Args: usageArgs(cobra.MaximumNArgs(1)), RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
if cmd.Flags().Changed("name") {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("supply name as an argument or --name, not both"))
|
||||
var org, template, team, localSource, remote string
|
||||
var clone, public, push bool
|
||||
command := &cobra.Command{
|
||||
Use: "create [<name>]",
|
||||
Aliases: []string{"new"},
|
||||
Short: "Create a repository",
|
||||
Long: `Create a repository for your account, or for an organization as ORG/NAME or with --org.
|
||||
|
||||
A new repository is empty unless --auto-init adds a first commit with a README and, optionally, a
|
||||
.gitignore and a license from the server's templates. --template copies another repository instead.
|
||||
|
||||
--source publishes an existing local Git repository: it adds the new repository as a remote of
|
||||
it (origin unless --remote names another), and --push pushes the current branch there.`,
|
||||
Example: `# Create a private repository with a README and an MIT license
|
||||
$ tea repo create project --private --auto-init --license MIT
|
||||
|
||||
# Create a repository in an organization from a template
|
||||
$ tea repo create acme/service --template acme/service-template
|
||||
|
||||
# Publish the local repository in the current directory
|
||||
$ tea repo create project --source . --push`,
|
||||
Args: usageArgs(cobra.MaximumNArgs(1)),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) > 0 {
|
||||
if cmd.Flags().Changed("name") {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("supply name as an argument or --name, not both"))
|
||||
}
|
||||
opts.Name = args[0]
|
||||
}
|
||||
opts.Name = args[0]
|
||||
}
|
||||
if err := a.rejectRepoFlag(cmd, func(owner string) string { return "tea repo create " + owner + "/" + cmp.Or(opts.Name, "NAME") }); err != nil {
|
||||
return err
|
||||
}
|
||||
if owner, name, ok := strings.Cut(opts.Name, "/"); ok {
|
||||
if cmd.Flags().Changed("org") {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("supply the owner as OWNER/NAME or --org, not both"))
|
||||
}
|
||||
org, opts.Name = owner, name
|
||||
}
|
||||
if cmd.Flags().Changed("team") && (org == "" || strings.TrimSpace(team) == "") {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--team requires --org and a team name"))
|
||||
}
|
||||
if cmd.Flags().Changed("template") && source == "" {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--template requires owner/repo or a repository URL"))
|
||||
}
|
||||
prompted, err := a.require(cmd, &opts.Name, "name")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateRepositoryName(opts.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(opts.Description) > 2048 {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("description cannot exceed 2048 bytes"))
|
||||
}
|
||||
if opts.DefaultBranch != "" {
|
||||
if err := validateBranch(opts.DefaultBranch); err != nil {
|
||||
if err := a.rejectRepoFlag(cmd, func(owner string) string { return "tea repo create " + owner + "/" + cmp.Or(opts.Name, "NAME") }); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
switch opts.TrustModel {
|
||||
case "", "committer", "collaborator", "collaborator+committer":
|
||||
default:
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--trust-model must be committer, collaborator, or collaborator+committer"))
|
||||
}
|
||||
if opts.ObjectFormat != "" && opts.ObjectFormat != "sha1" && opts.ObjectFormat != "sha256" {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--object-format must be sha1 or sha256"))
|
||||
}
|
||||
if source == "" && (opts.Gitignores != "" || opts.License != "" || opts.Readme != "" || opts.DefaultBranch != "") && !opts.AutoInit {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("initial content and default branch options require --auto-init"))
|
||||
}
|
||||
selected := *a
|
||||
if source != "" {
|
||||
for _, flag := range []string{"auto-init", "label-template", "gitignores", "license", "readme", "as-template", "trust-model", "object-format"} {
|
||||
if cmd.Flags().Changed(flag) {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("repository initialization options cannot be combined with --template"))
|
||||
if owner, name, ok := strings.Cut(opts.Name, "/"); ok {
|
||||
if cmd.Flags().Changed("org") {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("supply the owner as OWNER/NAME or --org, not both"))
|
||||
}
|
||||
org, opts.Name = owner, name
|
||||
}
|
||||
selected.options.repo = source
|
||||
} else {
|
||||
for _, flag := range []string{"copy-content", "copy-git-hooks", "copy-avatar", "copy-labels", "copy-topics", "copy-webhooks"} {
|
||||
if cmd.Flags().Changed(flag) {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("copy options require --template owner/repo"))
|
||||
}
|
||||
}
|
||||
}
|
||||
target, err := selected.resolve(cmd.Context(), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cmd.Flags().Changed("org") && strings.EqualFold(org, target.User) {
|
||||
if team != "" {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--team requires a repository owned by an organization"))
|
||||
}
|
||||
org = ""
|
||||
}
|
||||
if prompted {
|
||||
if err := a.confirm(cmd, "Create repository "+opts.Name+" on "+target.Host); err != nil {
|
||||
if err := validateCreateFlags(cmd, opts, org, team, template, localSource, clone); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var teamID int64
|
||||
if team != "" {
|
||||
teams, err := (services.Teams{Client: target.client}).ByName(cmd.Context(), org)
|
||||
if public {
|
||||
if cmd.Flags().Changed("private") {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--public and --private cannot be combined"))
|
||||
}
|
||||
opts.Private = false
|
||||
}
|
||||
if localSource != "" {
|
||||
if !filepath.IsAbs(localSource) {
|
||||
localSource = filepath.Join(a.options.cwd, localSource)
|
||||
}
|
||||
if err := a.checkPublishSource(cmd.Context(), localSource, remote); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
prompted, err := a.requireInput(cmd, &opts.Name, "name", "Repository name", func(name string) error { return validateRepositoryName(name) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
match, ok := teams[strings.ToLower(team)]
|
||||
if !ok {
|
||||
return clierr.Wrap(clierr.Usage, fmt.Errorf("team %q not found in organization %s", team, org))
|
||||
if err := validateRepositoryName(opts.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
teamID = match.ID
|
||||
}
|
||||
service := services.Repositories{Client: target.client}
|
||||
var item services.Repository
|
||||
if source != "" {
|
||||
templateOpts.Name = opts.Name
|
||||
templateOpts.Owner = org
|
||||
templateOpts.Description = opts.Description
|
||||
templateOpts.Private = opts.Private
|
||||
templateOpts.DefaultBranch = opts.DefaultBranch
|
||||
item, err = service.Generate(cmd.Context(), target.Repo, templateOpts)
|
||||
} else {
|
||||
item, err = service.Create(cmd.Context(), org, opts)
|
||||
selected := *a
|
||||
selected.options.repo = template
|
||||
target, err := selected.resolve(cmd.Context(), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !cmd.Flags().Changed("org") && strings.EqualFold(org, target.User) {
|
||||
if team != "" {
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--team requires a repository owned by an organization"))
|
||||
}
|
||||
org = ""
|
||||
}
|
||||
service := services.Repositories{Client: target.client}
|
||||
if err := validateTemplates(cmd.Context(), service, &opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if prompted {
|
||||
visibility := "public"
|
||||
if opts.Private {
|
||||
visibility = "private"
|
||||
}
|
||||
owner := cmp.Or(org, target.User)
|
||||
if err := a.confirmAction(cmd, terminal.Confirmation{Prompt: fmt.Sprintf("Create %s repository %s/%s on %s?", visibility, owner, opts.Name, target.Host), DefaultYes: true}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
var teamID int64
|
||||
if team != "" {
|
||||
teams, err := (services.Teams{Client: target.client}).ByName(cmd.Context(), org)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
match, ok := teams[strings.ToLower(team)]
|
||||
if !ok {
|
||||
return clierr.Wrap(clierr.Usage, fmt.Errorf("team %q not found in organization %s", team, org))
|
||||
}
|
||||
teamID = match.ID
|
||||
}
|
||||
var item services.Repository
|
||||
if template != "" {
|
||||
templateOpts.Name = opts.Name
|
||||
templateOpts.Owner = org
|
||||
templateOpts.Description = opts.Description
|
||||
templateOpts.Private = opts.Private
|
||||
templateOpts.DefaultBranch = opts.DefaultBranch
|
||||
item, err = service.Generate(cmd.Context(), target.Repo, templateOpts)
|
||||
} else {
|
||||
item, err = service.Create(cmd.Context(), org, opts)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
a.done(cmd, "Created repository %s", item.FullName)
|
||||
if teamID != 0 {
|
||||
if err := (services.Teams{Client: target.client}).SetRepository(cmd.Context(), teamID, item.FullName, true); err != nil {
|
||||
return fmt.Errorf("created %s, but could not grant team %q access: %w", item.FullName, team, err)
|
||||
}
|
||||
a.done(cmd, "Granted team %s access to %s", team, item.FullName)
|
||||
}
|
||||
target.Repo = item.FullName
|
||||
switch {
|
||||
case clone:
|
||||
_, err = a.cloneRepository(cmd, target, item, cloneOptions{upstream: "upstream"})
|
||||
case localSource != "":
|
||||
err = a.publishRepository(cmd, target, item, localSource, remote, push)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if a.options.json != "" {
|
||||
return a.writeJSON(cmd, item)
|
||||
}
|
||||
return a.printURL(cmd, item.URL)
|
||||
},
|
||||
}
|
||||
flags := command.Flags()
|
||||
flags.StringVar(&opts.Name, "name", "", "Repository name")
|
||||
_ = flags.MarkHidden("name")
|
||||
flags.StringVar(&org, "org", "", "Create the repository in this organization")
|
||||
flags.StringVar(&team, "team", "", "Grant this organization team access")
|
||||
flags.BoolVar(&clone, "clone", false, "Clone the new repository into the current directory")
|
||||
flags.StringVarP(&opts.Description, "description", "d", "", "Repository description")
|
||||
flags.BoolVar(&opts.Private, "private", false, "Make the repository private")
|
||||
flags.BoolVar(&public, "public", false, "Make the repository public (the default)")
|
||||
flags.BoolVar(&opts.AutoInit, "auto-init", false, "Add a first commit with a README")
|
||||
flags.StringVar(&opts.DefaultBranch, "default-branch", "", "Name of the default branch")
|
||||
flags.StringVar(&opts.Labels, "label-template", "", "Add the labels of this label set")
|
||||
flags.StringVarP(&opts.Gitignores, "gitignores", "g", "", "Comma-separated .gitignore templates (requires --auto-init)")
|
||||
flags.StringVarP(&opts.License, "license", "l", "", "License template (requires --auto-init)")
|
||||
flags.StringVar(&opts.Readme, "readme", "", "README template (requires --auto-init)")
|
||||
flags.BoolVar(&opts.Template, "as-template", false, "Make the new repository a template")
|
||||
enumFlag(command, &opts.TrustModel, "trust-model", "", "", "Signature trust model", "default", "collaborator", "committer", "collaboratorcommitter")
|
||||
enumFlag(command, &opts.ObjectFormat, "object-format", "", "", "Git object format", "sha1", "sha256")
|
||||
flags.StringVar(&template, "template", "", "Copy this template repository (OWNER/REPO or URL)")
|
||||
flags.BoolVar(&templateOpts.Content, "copy-content", true, "Copy the template's Git content")
|
||||
flags.BoolVar(&templateOpts.GitHooks, "copy-git-hooks", true, "Copy the template's Git hooks")
|
||||
flags.BoolVar(&templateOpts.Avatar, "copy-avatar", true, "Copy the template's avatar")
|
||||
flags.BoolVar(&templateOpts.Labels, "copy-labels", true, "Copy the template's labels")
|
||||
flags.BoolVar(&templateOpts.Topics, "copy-topics", true, "Copy the template's topics")
|
||||
flags.BoolVar(&templateOpts.Webhooks, "copy-webhooks", false, "Copy the template's webhooks")
|
||||
flags.StringVar(&localSource, "source", "", "Publish the local Git repository in this directory")
|
||||
_ = command.MarkFlagDirname("source")
|
||||
flags.StringVar(&remote, "remote-name", "origin", "Name of the new repository's remote in --source")
|
||||
flags.BoolVar(&push, "push", false, "Push the current branch of --source")
|
||||
_ = command.RegisterFlagCompletionFunc("org", a.completeOrgs())
|
||||
_ = command.RegisterFlagCompletionFunc("template", a.completeRepos())
|
||||
_ = command.RegisterFlagCompletionFunc("license", a.completeTemplates(services.Repositories.Licenses))
|
||||
_ = command.RegisterFlagCompletionFunc("gitignores", commaSeparated(a.completeTemplates(services.Repositories.Gitignores)))
|
||||
_ = command.RegisterFlagCompletionFunc("label-template", a.completeTemplates(services.Repositories.LabelTemplates))
|
||||
return command
|
||||
}
|
||||
|
||||
// validateCreateFlags rejects combinations of repo create flags that cannot work, before any request.
|
||||
func validateCreateFlags(cmd *cobra.Command, opts services.RepositoryOptions, org, team, template, localSource string, clone bool) error {
|
||||
changed := func(names ...string) bool { return slices.ContainsFunc(names, cmd.Flags().Changed) }
|
||||
switch {
|
||||
case changed("team") && (org == "" || strings.TrimSpace(team) == ""):
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--team requires --org and a team name"))
|
||||
case changed("template") && template == "":
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--template requires owner/repo or a repository URL"))
|
||||
case len(opts.Description) > 2048:
|
||||
return clierr.Wrap(clierr.Usage, errors.New("description cannot exceed 2048 bytes"))
|
||||
case template == "" && (opts.Gitignores != "" || opts.License != "" || opts.Readme != "" || opts.DefaultBranch != "") && !opts.AutoInit:
|
||||
return clierr.Wrap(clierr.Usage, errors.New("initial content and default branch options require --auto-init"))
|
||||
case template != "" && changed("auto-init", "label-template", "gitignores", "license", "readme", "as-template", "trust-model", "object-format"):
|
||||
return clierr.Wrap(clierr.Usage, errors.New("repository initialization options cannot be combined with --template"))
|
||||
case template == "" && changed("copy-content", "copy-git-hooks", "copy-avatar", "copy-labels", "copy-topics", "copy-webhooks"):
|
||||
return clierr.Wrap(clierr.Usage, errors.New("copy options require --template owner/repo"))
|
||||
case localSource != "" && (clone || template != "" || opts.AutoInit):
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--source publishes a local repository; it cannot be combined with --clone, --template or --auto-init"))
|
||||
case localSource == "" && changed("push", "remote-name"):
|
||||
return clierr.Wrap(clierr.Usage, errors.New("--push and --remote-name require --source"))
|
||||
}
|
||||
if opts.DefaultBranch != "" {
|
||||
return validateBranch(opts.DefaultBranch)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateTemplates checks the license, .gitignore and label templates of opts against the
|
||||
// server's lists, and spells them as the server does.
|
||||
func validateTemplates(ctx context.Context, service services.Repositories, opts *services.RepositoryOptions) error {
|
||||
for _, field := range []struct {
|
||||
value *string
|
||||
kind, flag string
|
||||
list func(services.Repositories, context.Context) ([]string, error)
|
||||
multi bool
|
||||
}{
|
||||
{&opts.License, "license", "license", services.Repositories.Licenses, false},
|
||||
{&opts.Gitignores, ".gitignore template", "gitignores", services.Repositories.Gitignores, true},
|
||||
{&opts.Labels, "label template", "label-template", services.Repositories.LabelTemplates, false},
|
||||
} {
|
||||
if *field.value == "" {
|
||||
continue
|
||||
}
|
||||
known, err := field.list(service, ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if teamID != 0 {
|
||||
if err := (services.Teams{Client: target.client}).SetRepository(cmd.Context(), teamID, item.FullName, true); err != nil {
|
||||
return fmt.Errorf("created %s, but could not grant team %q access: %w", item.FullName, team, err)
|
||||
names := []string{*field.value}
|
||||
if field.multi {
|
||||
names = strings.Split(*field.value, ",")
|
||||
}
|
||||
for i, name := range names {
|
||||
at := slices.IndexFunc(known, func(k string) bool { return strings.EqualFold(k, strings.TrimSpace(name)) })
|
||||
if at < 0 {
|
||||
return clierr.Wrap(clierr.Usage, fmt.Errorf("unknown %s %q; complete --%s with Tab to see the server's %ss", field.kind, strings.TrimSpace(name), field.flag, field.kind))
|
||||
}
|
||||
names[i] = known[at]
|
||||
}
|
||||
if err := writeRecord(a, cmd, item, repositoryRecord); err != nil || !clone {
|
||||
return err
|
||||
}
|
||||
target.Repo = item.FullName
|
||||
_, err = a.cloneRepository(cmd, target, item, cloneOptions{upstream: "upstream"})
|
||||
return err
|
||||
}}
|
||||
flags := command.Flags()
|
||||
flags.StringVar(&opts.Name, "name", "", "Repository name")
|
||||
flags.StringVar(&org, "org", "", "Create under this organization")
|
||||
flags.StringVar(&team, "team", "", "Grant this organization team access (requires --org)")
|
||||
flags.BoolVar(&clone, "clone", false, "Clone the new repository into the current directory")
|
||||
flags.StringVar(&opts.Description, "description", "", "Repository description")
|
||||
flags.BoolVar(&opts.Private, "private", false, "Create a private repository")
|
||||
flags.BoolVar(&opts.AutoInit, "auto-init", false, "Initialize repository with a README")
|
||||
flags.StringVar(&opts.DefaultBranch, "default-branch", "", "Initial default branch")
|
||||
flags.StringVar(&opts.Labels, "label-template", "", "Named Gitea label set")
|
||||
flags.StringVar(&opts.Gitignores, "gitignores", "", "Comma-separated gitignore templates (requires --auto-init)")
|
||||
flags.StringVar(&opts.License, "license", "", "License template (requires --auto-init)")
|
||||
flags.StringVar(&opts.Readme, "readme", "", "README template (requires --auto-init)")
|
||||
flags.BoolVar(&opts.Template, "as-template", false, "Make the new repository a reusable template")
|
||||
flags.StringVar(&opts.TrustModel, "trust-model", "", "Signature trust model")
|
||||
flags.StringVar(&opts.ObjectFormat, "object-format", "", "Git object format: sha1 or sha256")
|
||||
flags.StringVar(&source, "template", "", "Source template owner/repo or repository URL")
|
||||
flags.BoolVar(&templateOpts.Content, "copy-content", true, "Copy template Git content")
|
||||
flags.BoolVar(&templateOpts.GitHooks, "copy-git-hooks", true, "Copy template Git hooks")
|
||||
flags.BoolVar(&templateOpts.Avatar, "copy-avatar", true, "Copy template avatar")
|
||||
flags.BoolVar(&templateOpts.Labels, "copy-labels", true, "Copy template labels")
|
||||
flags.BoolVar(&templateOpts.Topics, "copy-topics", true, "Copy template topics")
|
||||
flags.BoolVar(&templateOpts.Webhooks, "copy-webhooks", false, "Copy template webhooks")
|
||||
return command
|
||||
*field.value = strings.Join(names, ",")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// completeTemplates completes the names that list returns, such as the server's licenses.
|
||||
func (a *application) completeTemplates(list func(services.Repositories, context.Context) ([]string, error)) cobra.CompletionFunc {
|
||||
return a.apiCompletion(false, func(ctx context.Context, t target, _ string) ([]cobra.Completion, error) {
|
||||
names, err := list(services.Repositories{Client: t.client}, ctx)
|
||||
return describe(names, func(name string) (string, string) { return name, "" }), err
|
||||
})
|
||||
}
|
||||
|
||||
// checkPublishSource fails unless dir is a Git working tree without a remote named remote.
|
||||
func (a *application) checkPublishSource(ctx context.Context, dir, remote string) error {
|
||||
if inside, _ := a.gitProbe().Output(ctx, dir, false, "rev-parse", "--is-inside-work-tree"); inside != "true" {
|
||||
return clierr.Wrap(clierr.Usage, fmt.Errorf("--source %s is not a Git repository; run git init first", dir))
|
||||
}
|
||||
if _, err := a.gitProbe().Output(ctx, dir, false, "remote", "get-url", remote); err == nil {
|
||||
return clierr.Wrap(clierr.Usage, fmt.Errorf("--source %s already has a remote %s; pass --remote-name", dir, remote))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// publishRepository adds item as the remote of the local repository in dir and, with push,
|
||||
// pushes the current branch to it.
|
||||
func (a *application) publishRepository(cmd *cobra.Command, t target, item services.Repository, dir, remote string, push bool) error {
|
||||
url, ssh, err := cloneURLForProtocol(item.CloneURL, item.SSHURL, t.gitProtocol)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := a.gitIn(cmd.Context(), dir, "remote", "add", "--", remote, url); err != nil {
|
||||
return fmt.Errorf("created %s, but could not add it as Git remote %s: %w", item.FullName, remote, err)
|
||||
}
|
||||
a.done(cmd, "Added Git remote %s for %s", remote, item.FullName)
|
||||
if !push {
|
||||
return nil
|
||||
}
|
||||
credentials, err := a.gitTransportArguments(t, ssh)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := a.gitIn(cmd.Context(), dir, append(credentials, "push", "--quiet", "--set-upstream", remote, "HEAD")...); err != nil {
|
||||
return fmt.Errorf("created %s, but could not push to it: %w", item.FullName, err)
|
||||
}
|
||||
a.done(cmd, "Pushed the current branch to %s", item.FullName)
|
||||
return nil
|
||||
}
|
||||
Reference in new issue
Block a user