func createRun()

in commands/release/create/create.go [268:512]


func createRun(opts *CreateOpts) error {
	client, err := opts.HTTPClient()
	if err != nil {
		return err
	}

	repo, err := opts.BaseRepo()
	if err != nil {
		return err
	}
	color := opts.IO.Color()
	var tag *gitlab.Tag
	var resp *gitlab.Response

	if opts.Ref == "" {
		opts.IO.Log(color.ProgressIcon(), "Validating tag", opts.TagName)
		tag, resp, err = client.Tags.GetTag(repo.FullName(), opts.TagName)
		if err != nil && resp != nil && resp.StatusCode != http.StatusNotFound {
			return cmdutils.WrapError(err, "could not fetch tag")
		}
		if tag == nil && resp != nil && resp.StatusCode == http.StatusNotFound {
			opts.IO.Log(color.DotWarnIcon(), "Tag does not exist.")
			opts.IO.Log(color.DotWarnIcon(), "No ref provided. Creating the tag from the latest state of the default branch.")
			project, err := repo.Project(client)
			if err == nil {
				opts.IO.Logf("%s using default branch %q as ref\n", color.ProgressIcon(), project.DefaultBranch)
				opts.Ref = project.DefaultBranch
			}
		}
		// new line
		opts.IO.Log()
	}

	if opts.IO.PromptEnabled() && !opts.NoteProvided {
		editorCommand, err := cmdutils.GetEditor(opts.Config)
		if err != nil {
			return err
		}

		var tagDescription string
		var generatedChangelog string
		if tag == nil {
			tag, _, _ = client.Tags.GetTag(repo.FullName(), opts.TagName)
		}
		if tag != nil {
			tagDescription = tag.Message
		}
		if opts.RepoOverride == "" {
			headRef := opts.TagName
			if tagDescription == "" {
				if opts.Ref != "" {
					headRef = opts.Ref
					brCfg := git.ReadBranchConfig(opts.Ref)
					if brCfg.MergeRef != "" {
						headRef = brCfg.MergeRef
					}
				} else {
					headRef = "HEAD"
				}
			}

			if prevTag, err := detectPreviousTag(headRef); err == nil {
				commits, _ := changelogForRange(fmt.Sprintf("%s..%s", prevTag, headRef))
				generatedChangelog = generateChangelog(commits)
			}
		}

		editorOptions := []string{noteOptionsNames[noteOptMyOwn]}
		if generatedChangelog != "" {
			editorOptions = append(editorOptions, noteOptionsNames[noteOptCommitLog])
		}
		if tagDescription != "" {
			editorOptions = append(editorOptions, noteOptionsNames[noteOptGitTagMessage])
		}
		editorOptions = append(editorOptions, noteOptionsNames[noteOptLeaveBlank])

		qs := []*survey.Question{
			{
				Name: "name",
				Prompt: &survey.Input{
					Message: "Release title (optional)",
					Default: opts.Name,
				},
			},
			{
				Name: "releaseNotesAction",
				Prompt: &survey.Select{
					Message: "Release notes",
					Options: editorOptions,
				},
			},
		}
		err = prompt.Ask(qs, opts)
		if err != nil {
			return fmt.Errorf("could not prompt: %w", err)
		}

		var openEditor bool
		var editorContents string

		switch opts.ReleaseNotesAction {
		case noteOptionsNames[noteOptMyOwn]:
			openEditor = true
		case noteOptionsNames[noteOptCommitLog]:
			openEditor = true
			editorContents = generatedChangelog
		case noteOptionsNames[noteOptGitTagMessage]:
			openEditor = true
			editorContents = tagDescription
		case noteOptionsNames[noteOptLeaveBlank]:
			openEditor = false
		default:
			return fmt.Errorf("invalid action: %v", opts.ReleaseNotesAction)
		}

		if openEditor {
			txt, err := surveyext.Edit(editorCommand, "*.md", editorContents, opts.IO.In, opts.IO.StdOut, opts.IO.StdErr, nil)
			if err != nil {
				return err
			}
			opts.Notes = txt
		}
	}
	start := time.Now()

	opts.IO.Logf("%s Creating or updating release %s=%s %s=%s\n",
		color.ProgressIcon(),
		color.Blue("repo"), repo.FullName(),
		color.Blue("tag"), opts.TagName)

	release, resp, err := client.Releases.GetRelease(repo.FullName(), opts.TagName)
	if err != nil && (resp == nil || (resp.StatusCode != http.StatusForbidden && resp.StatusCode != http.StatusNotFound)) {
		return releaseFailedErr(err, start)
	}

	var releasedAt time.Time

	if opts.ReleasedAt != "" {
		// Parse the releasedAt to the expected format of the API
		// From the API docs "Expected in ISO 8601 format (2019-03-15T08:00:00Z)".
		releasedAt, err = time.Parse(time.RFC3339[:len(opts.ReleasedAt)], opts.ReleasedAt)
		if err != nil {
			return releaseFailedErr(err, start)
		}
	}

	if opts.Name == "" {
		opts.Name = opts.TagName
	}

	if (resp.StatusCode == http.StatusForbidden || resp.StatusCode == http.StatusNotFound) || release == nil {
		createOpts := &gitlab.CreateReleaseOptions{
			Name:    &opts.Name,
			TagName: &opts.TagName,
		}

		if opts.Notes != "" {
			createOpts.Description = &opts.Notes
		}

		if opts.Ref != "" {
			createOpts.Ref = &opts.Ref
		}

		if opts.TagMessage != "" {
			createOpts.TagMessage = &opts.TagMessage
		}

		if opts.ReleasedAt != "" {
			createOpts.ReleasedAt = &releasedAt
		}

		if len(opts.Milestone) > 0 {
			createOpts.Milestones = &opts.Milestone
		}

		release, _, err = client.Releases.CreateRelease(repo.FullName(), createOpts)
		if err != nil {
			return releaseFailedErr(err, start)
		}
		opts.IO.Logf("%s Release created:\t%s=%s\n", color.GreenCheck(),
			color.Blue("url"), release.Links.Self)
	} else {
		if opts.NoUpdate {
			return releaseFailedErr(fmt.Errorf("release for tag %q already exists and --no-update flag was specified", opts.TagName), start)
		}

		updateOpts := &gitlab.UpdateReleaseOptions{
			Name: &opts.Name,
		}
		if opts.Notes != "" {
			updateOpts.Description = &opts.Notes
		}

		if opts.ReleasedAt != "" {
			updateOpts.ReleasedAt = &releasedAt
		}

		if len(opts.Milestone) > 0 {
			updateOpts.Milestones = &opts.Milestone
		}

		release, _, err = client.Releases.UpdateRelease(repo.FullName(), opts.TagName, updateOpts)
		if err != nil {
			return releaseFailedErr(err, start)
		}

		opts.IO.Logf("%s Release updated\t%s=%s\n", color.GreenCheck(),
			color.Blue("url"), release.Links.Self)
	}

	// upload files and create asset links
	err = releaseutils.CreateReleaseAssets(opts.IO, client, opts.AssetFiles, opts.AssetLinks, repo.FullName(), release.TagName)
	if err != nil {
		return releaseFailedErr(err, start)
	}

	if opts.NoCloseMilestone {
		opts.IO.Logf("%s Skipping closing milestones\n", color.GreenCheck())
	} else {
		if len(opts.Milestone) > 0 {
			// close all associated milestones
			for _, milestone := range opts.Milestone {
				opts.IO.StartSpinner("Closing milestone %q", milestone)
				err := closeMilestone(opts, milestone)
				opts.IO.StopSpinner("")
				if err != nil {
					opts.IO.Log(color.FailedIcon(), err.Error())
				} else {
					opts.IO.Logf("%s Closed milestone %q\n", color.GreenCheck(), milestone)
				}
			}
		}
	}
	opts.IO.Logf(color.Bold("%s Release succeeded after %0.2f seconds.\n"), color.GreenCheck(), time.Since(start).Seconds())

	if opts.PublishToCatalog {
		err = catalog.Publish(opts.IO, client, repo.FullName(), release.TagName)
		if err != nil {
			return cmdutils.WrapError(err, "failed to publish the release to the GitLab CI/CD catalog.")
		}
	}

	return nil
}