func fixReleaseNotes()

in cmd/krel/cmd/release_notes.go [1029:1208]


func fixReleaseNotes(workDir string, releaseNotes *notes.ReleaseNotes) error {
	// Get data to record the session
	userEmail, err := git.GetUserEmail()
	if err != nil {
		return errors.Wrap(err, "getting local user's email")
	}
	userName, err := git.GetUserName()
	if err != nil {
		return errors.Wrap(err, "getting local user's name")
	}

	// Check the workDir before going further
	if !util.Exists(workDir) {
		return errors.New("map directory does not exist")
	}

	// Create the new session struct
	session := &sessionData{
		UserEmail: userEmail,
		UserName:  userName,
		Date:      time.Now().UTC().Unix(),
		Path:      filepath.Join(workDir, mapsSessionDirectory),
	}

	// Read the list of all PRs we've processed so far
	pullRequestChecklist, err := readFixSessions(filepath.Join(workDir, mapsSessionDirectory))
	if err != nil {
		return errors.Wrapf(err, "reading previous session data")
	}

	// Greet the user with basic instructions
	greetingMessage := "\nWelcome to the Kubernetes Release Notes editing tool!\n\n"
	greetingMessage += "This tool will allow you to review and edit all the release\n"
	greetingMessage += "notes submitted by the Kubernetes contributors before publishing\n"
	greetingMessage += "the updated draft.\n\n"
	greetingMessage += "The flow will show each of the release notes that need to be\n"
	greetingMessage += "reviewed once and you can choose to edit it or not.\n\n"
	greetingMessage += "After you choose, it will be marked as reviewed and will not\n"
	greetingMessage += "be shown during the next sessions unless you choose to do a\n"
	greetingMessage += "full review of all notes.\n\n"
	greetingMessage += "You can hit Ctrl+C at any time to exit the review process\n"
	greetingMessage += "and submit the draft PR with the revisions made so far.\n\n"
	fmt.Print(greetingMessage)

	// Ask the user if they want to continue the last session o fix all notes
	continueFromLastSession := true
	if len(pullRequestChecklist) > 0 {
		_, continueFromLastSession, err = util.Ask("Would you like to continue from the last session? (Y/n)", "y:Y:yes|n:N:no|y", 10)
	} else {
		_, _, err = util.Ask("Press enter to start editing", "y:Y:yes|n:N:no|y", 10)
	}
	if err != nil {
		return errors.Wrap(err, "asking to retrieve last session")
	}

	// Bring up the provider
	provider, err := notes.NewProviderFromInitString(workDir)
	if err != nil {
		return errors.Wrap(err, "while getting map provider for current notes")
	}

	const (
		spacer = "    │ "
	)

	// Cycle all gathered release notes
	for pr, note := range releaseNotes.ByPR() {
		contentHash, err := note.ContentHash()
		noteReviewed := false
		if err != nil {
			return errors.Wrapf(err, "getting the content hash for PR#%d", pr)
		}
		// We'll skip editing if the Releas Note has been reviewed
		if _, ok := pullRequestChecklist[pr]; ok &&
			// and if we chose not to edit all
			continueFromLastSession &&
			// and if the not has not been modified in GutHub
			contentHash == pullRequestChecklist[pr] {
			logrus.Debugf("Pull Request %d already reviewed", pr)
			continue
		}
		title := fmt.Sprintf("Release Note for PR %d:", pr)
		fmt.Println(nl + title)
		fmt.Println(strings.Repeat("=", len(title)))
		fmt.Printf("Pull Request URL: %skubernetes/kubernetes/pull/%d%s", github.GitHubURL, pr, nl)
		noteMaps, err := provider.GetMapsForPR(pr)
		if err != nil {
			return errors.Wrapf(err, "while getting map for PR #%d", pr)
		}

		// Capture the original note values to compare
		originalNote := &notes.ReleaseNote{
			Text:           note.Text,
			Author:         note.Author,
			Areas:          note.Areas,
			Kinds:          note.Kinds,
			SIGs:           note.SIGs,
			Feature:        note.Feature,
			ActionRequired: note.ActionRequired,
			Documentation:  note.Documentation,
			DoNotPublish:   note.DoNotPublish,
		}

		if noteMaps != nil {
			fmt.Println("✨ Note contents was previously modified with a map")
			for _, noteMap := range noteMaps {
				if err := note.ApplyMap(noteMap, true); err != nil {
					return errors.Wrapf(err, "applying notemap for PR #%d", pr)
				}
			}
		}

		fmt.Println(pointIfChanged("Author", note.Author, originalNote.Author), "@"+note.Author)
		fmt.Println(pointIfChanged("SIGs", note.SIGs, originalNote.SIGs), note.SIGs)
		fmt.Println(pointIfChanged("Kinds", note.Kinds, originalNote.Kinds), note.Kinds)
		fmt.Println(pointIfChanged("Areas", note.Areas, originalNote.Areas), note.Areas)
		fmt.Println(pointIfChanged("Feature", note.Feature, originalNote.Feature), note.Feature)
		fmt.Println(pointIfChanged("ActionRequired", note.ActionRequired, originalNote.ActionRequired), note.ActionRequired)
		fmt.Println(pointIfChanged("DoNotPublish", note.DoNotPublish, originalNote.DoNotPublish), note.DoNotPublish)
		// TODO: Implement note.Documentation

		// Wrap the note for better readability on the terminal
		fmt.Println(pointIfChanged("Text", note.Text, originalNote.Text))
		text := util.WrapText(note.Text, 80)
		fmt.Println(spacer + strings.ReplaceAll(text, nl, nl+spacer))

		_, choice, err := util.Ask(fmt.Sprintf("\n- Fix note for PR #%d? (y/N)", note.PrNumber), "y:Y:yes|n:N:no|n", 10)
		if err != nil {
			// If the user cancelled with ctr+c exit and continue the PR flow
			if err.(util.UserInputError).IsCtrlC() {
				logrus.Info("Input cancelled, exiting edit flow")
				return nil
			}
			return errors.Wrap(err, "while asking to edit release note")
		}

		noteReviewed = true
		if choice {
			for {
				retry, err := editReleaseNote(pr, workDir, originalNote, note)
				if err == nil {
					break
				}
				// If it's a user error (like yaml error) we can try again
				if retry {
					logrus.Error(err)
					_, retryEditingChoice, err := util.Ask(
						fmt.Sprintf("\n- An error occurred while editing PR #%d. Try again?", note.PrNumber),
						"y:yes|n:no", 10,
					)
					if err != nil {
						return errors.Wrap(err, "while asking to re-edit release note")
					}
					// If user chooses not to fix the faulty yaml, do not mark as fixed
					if !retryEditingChoice {
						noteReviewed = false
						break
					}
				} else {
					return errors.Wrap(err, "while editing release note")
				}
			}
		}
		// If the note was reviewed, add the PR to the session file:
		if noteReviewed {
			pullRequestChecklist[note.PrNumber] = contentHash
			session.PullRequests = append(session.PullRequests, struct {
				Number int    `json:"nr"`
				Hash   string `json:"hash"`
			}{
				Number: note.PrNumber,
				Hash:   contentHash,
			})
			if err := session.Save(); err != nil {
				return errors.Wrap(err, "while saving editing session data")
			}
		}
	}
	return nil
}