From 18fd743da40a601ad1bb37d1416c591a0f1e56bc Mon Sep 17 00:00:00 2001 From: Danny Ranson Date: Wed, 10 Apr 2024 14:51:26 +0100 Subject: [PATCH 1/5] prompt for unchanged or empty description --- cmd/create_prs/create_prs.go | 23 ++++++++ cmd/create_prs/create_prs_test.go | 83 +++++++++++++++++++++++++++++ internal/prompt/prompt.go | 13 ++++- internal/testsupport/testsupport.go | 15 ++++++ 4 files changed, 132 insertions(+), 2 deletions(-) diff --git a/cmd/create_prs/create_prs.go b/cmd/create_prs/create_prs.go index 4f58e3a..4048b17 100644 --- a/cmd/create_prs/create_prs.go +++ b/cmd/create_prs/create_prs.go @@ -16,6 +16,8 @@ package create_prs import ( + "fmt" + "github.com/skyscanner/turbolift/internal/prompt" "os" "path" "time" @@ -32,6 +34,7 @@ import ( var ( gh github.GitHub = github.NewRealGitHub() g git.Git = git.NewRealGit() + p prompt.Prompt = prompt.NewRealPrompt() ) var ( @@ -68,6 +71,11 @@ func run(c *cobra.Command, _ []string) { readCampaignActivity.EndWithFailure(err) return } + if prDescriptionUnchanged(dir) { + if !p.AskConfirm(fmt.Sprintf("It looks like the PR title and/or description has not been updated in %s. Are you sure you want to proceed?", prDescriptionFile)) { + return + } + } readCampaignActivity.EndWithSuccess() doneCount := 0 @@ -131,3 +139,18 @@ func run(c *cobra.Command, _ []string) { logger.Warnf("turbolift create-prs completed with %s %s(%s, %s, %s)\n", colors.Red("errors"), colors.Normal(), colors.Green(doneCount, " OK"), colors.Yellow(skippedCount, " skipped"), colors.Red(errorCount, " errored")) } } + +func prDescriptionUnchanged(dir *campaign.Campaign) bool { + originalPrTitle := fmt.Sprintf("TODO: Title of Pull Request (%s)", dir.Name) + originalPrBody := `TODO: This file will serve as both a README and the description of the PR. Describe the pull request using markdown in this file. Make it clear why the change is being made, and make suggestions for anything that the reviewer may need to do. + +By approving this PR, you are confirming that you have adequately and effectively reviewed this change. + +## How this change was made +TODO: Describe the approach that was used to select repositories for this change +TODO: Describe any shell commands, scripts, manual operations, etc, that were used to make changes + + +This PR was generated using [turbolift](https://github.com/Skyscanner/turbolift).` + return dir.PrTitle == originalPrTitle || dir.PrBody == originalPrBody || dir.PrTitle == "" +} diff --git a/cmd/create_prs/create_prs_test.go b/cmd/create_prs/create_prs_test.go index 398b817..822c7d0 100644 --- a/cmd/create_prs/create_prs_test.go +++ b/cmd/create_prs/create_prs_test.go @@ -19,11 +19,56 @@ import ( "bytes" "github.com/skyscanner/turbolift/internal/git" "github.com/skyscanner/turbolift/internal/github" + "github.com/skyscanner/turbolift/internal/prompt" "github.com/skyscanner/turbolift/internal/testsupport" "github.com/stretchr/testify/assert" "testing" ) +func TestItWarnsIfDescriptionFileTemplateIsUnchanged(t *testing.T) { + fakeGitHub := github.NewAlwaysFailsFakeGitHub() + gh = fakeGitHub + fakeGit := git.NewAlwaysSucceedsFakeGit() + g = fakeGit + fakePrompt := prompt.NewFakePromptNo() + p = fakePrompt + + dir := testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2") + testsupport.UseDefaultPrDescription(dir) + + out, err := runCommand() + assert.NoError(t, err) + assert.NotContains(t, out, "Creating PR in org/repo1") + assert.NotContains(t, out, "Creating PR in org/repo2") + assert.NotContains(t, out, "turbolift create-prs completed") + assert.NotContains(t, out, "2 OK, 0 skipped") + + fakePrompt.AssertCalledWith(t, "It looks like the PR title and/or description has not been updated in README.md. Are you sure you want to proceed?") +} + +func TestItWarnsIfDescriptionFileIsEmpty(t *testing.T) { + fakeGitHub := github.NewAlwaysFailsFakeGitHub() + gh = fakeGitHub + fakeGit := git.NewAlwaysSucceedsFakeGit() + g = fakeGit + fakePrompt := prompt.NewFakePromptNo() + p = fakePrompt + + customDescriptionFileName := "custom.md" + + testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2") + testsupport.CreateOrUpdatePrDescriptionFile(customDescriptionFileName, "", "") + + out, err := runCommandWithAlternativeDescriptionFile(customDescriptionFileName) + assert.NoError(t, err) + assert.NotContains(t, out, "Creating PR in org/repo1") + assert.NotContains(t, out, "Creating PR in org/repo2") + assert.NotContains(t, out, "turbolift create-prs completed") + assert.NotContains(t, out, "2 OK, 0 skipped") + + fakePrompt.AssertCalledWith(t, "It looks like the PR title and/or description has not been updated in custom.md. Are you sure you want to proceed?") +} + func TestItLogsCreatePrErrorsButContinuesToTryAll(t *testing.T) { fakeGitHub := github.NewAlwaysFailsFakeGitHub() gh = fakeGitHub @@ -106,6 +151,31 @@ func TestItLogsCreateDraftPr(t *testing.T) { }) } +func TestItCreatesPrsFromAlternativeDescriptionFile(t *testing.T) { + fakeGitHub := github.NewAlwaysSucceedsFakeGitHub() + gh = fakeGitHub + fakeGit := git.NewAlwaysSucceedsFakeGit() + g = fakeGit + + customDescriptionFileName := "custom.md" + + testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2") + testsupport.CreateOrUpdatePrDescriptionFile(customDescriptionFileName, "custom PR title", "custom PR body") + + out, err := runCommandWithAlternativeDescriptionFile(customDescriptionFileName) + assert.NoError(t, err) + assert.Contains(t, out, "Reading campaign data (repos.txt, custom.md)") + assert.Contains(t, out, "Creating PR in org/repo1") + assert.Contains(t, out, "Creating PR in org/repo2") + assert.Contains(t, out, "turbolift create-prs completed") + assert.Contains(t, out, "2 OK, 0 skipped") + + fakeGitHub.AssertCalledWith(t, [][]string{ + {"work/org/repo1", "custom PR title"}, + {"work/org/repo2", "custom PR title"}, + }) +} + func runCommand() (string, error) { cmd := NewCreatePRsCmd() outBuffer := bytes.NewBufferString("") @@ -118,6 +188,19 @@ func runCommand() (string, error) { return outBuffer.String(), nil } +func runCommandWithAlternativeDescriptionFile(fileName string) (string, error) { + cmd := NewCreatePRsCmd() + prDescriptionFile = fileName + outBuffer := bytes.NewBufferString("") + cmd.SetOut(outBuffer) + err := cmd.Execute() + + if err != nil { + return outBuffer.String(), err + } + return outBuffer.String(), nil +} + func runCommandDraft() (string, error) { cmd := NewCreatePRsCmd() isDraft = true diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index d9f1876..95da677 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -2,8 +2,10 @@ package prompt import ( "strings" + "testing" "github.com/manifoldco/promptui" + "github.com/stretchr/testify/assert" ) type Prompt interface { @@ -49,12 +51,19 @@ func (f FakePromptYes) AskConfirm(_ string) bool { } // Mock Prompt that always returns false -type FakePromptNo struct{} +type FakePromptNo struct { + call string +} func NewFakePromptNo() *FakePromptNo { return &FakePromptNo{} } -func (f FakePromptNo) AskConfirm(_ string) bool { +func (f *FakePromptNo) AskConfirm(question string) bool { + f.call = question return false } + +func (f *FakePromptNo) AssertCalledWith(t *testing.T, expected string) { + assert.Equal(t, expected, f.call) +} diff --git a/internal/testsupport/testsupport.go b/internal/testsupport/testsupport.go index 062ddf6..b7f2c58 100644 --- a/internal/testsupport/testsupport.go +++ b/internal/testsupport/testsupport.go @@ -81,3 +81,18 @@ func CreateOrUpdatePrDescriptionFile(filename string, prTitle string, prBody str panic(err) } } + +func UseDefaultPrDescription(dirName string) { + originalPrTitle := fmt.Sprintf("TODO: Title of Pull Request (%s)", dirName) + originalPrBody := `TODO: This file will serve as both a README and the description of the PR. Describe the pull request using markdown in this file. Make it clear why the change is being made, and make suggestions for anything that the reviewer may need to do. + +By approving this PR, you are confirming that you have adequately and effectively reviewed this change. + +## How this change was made +TODO: Describe the approach that was used to select repositories for this change +TODO: Describe any shell commands, scripts, manual operations, etc, that were used to make changes + + +This PR was generated using [turbolift](https://github.com/Skyscanner/turbolift).` + CreateOrUpdatePrDescriptionFile("README.md", originalPrTitle, originalPrBody) +} From 1fcc16bb060b72fe9acce6dece2805ef6833b5c2 Mon Sep 17 00:00:00 2001 From: Danny Ranson Date: Wed, 14 Aug 2024 11:20:48 +0100 Subject: [PATCH 2/5] check for todos only --- cmd/create_prs/create_prs.go | 18 +++++------------- cmd/create_prs/create_prs_test.go | 4 ++-- internal/testsupport/testsupport.go | 15 +++------------ 3 files changed, 10 insertions(+), 27 deletions(-) diff --git a/cmd/create_prs/create_prs.go b/cmd/create_prs/create_prs.go index 4048b17..1e3bc7d 100644 --- a/cmd/create_prs/create_prs.go +++ b/cmd/create_prs/create_prs.go @@ -20,6 +20,7 @@ import ( "github.com/skyscanner/turbolift/internal/prompt" "os" "path" + "strings" "time" "github.com/spf13/cobra" @@ -72,7 +73,7 @@ func run(c *cobra.Command, _ []string) { return } if prDescriptionUnchanged(dir) { - if !p.AskConfirm(fmt.Sprintf("It looks like the PR title and/or description has not been updated in %s. Are you sure you want to proceed?", prDescriptionFile)) { + if !p.AskConfirm(fmt.Sprintf("It looks like the PR title and/or description may not have been updated in %s. Are you sure you want to proceed?", prDescriptionFile)) { return } } @@ -141,16 +142,7 @@ func run(c *cobra.Command, _ []string) { } func prDescriptionUnchanged(dir *campaign.Campaign) bool { - originalPrTitle := fmt.Sprintf("TODO: Title of Pull Request (%s)", dir.Name) - originalPrBody := `TODO: This file will serve as both a README and the description of the PR. Describe the pull request using markdown in this file. Make it clear why the change is being made, and make suggestions for anything that the reviewer may need to do. - -By approving this PR, you are confirming that you have adequately and effectively reviewed this change. - -## How this change was made -TODO: Describe the approach that was used to select repositories for this change -TODO: Describe any shell commands, scripts, manual operations, etc, that were used to make changes - - -This PR was generated using [turbolift](https://github.com/Skyscanner/turbolift).` - return dir.PrTitle == originalPrTitle || dir.PrBody == originalPrBody || dir.PrTitle == "" + originalPrTitleTodo := fmt.Sprintf("TODO: Title of Pull Request (%s)", dir.Name) + originalPrBodyTodo := "TODO: This file will serve as both a README and the description of the PR." + return dir.PrTitle == originalPrTitleTodo || strings.Contains(dir.PrBody, originalPrBodyTodo) || dir.PrTitle == "" } diff --git a/cmd/create_prs/create_prs_test.go b/cmd/create_prs/create_prs_test.go index 822c7d0..24bd371 100644 --- a/cmd/create_prs/create_prs_test.go +++ b/cmd/create_prs/create_prs_test.go @@ -43,7 +43,7 @@ func TestItWarnsIfDescriptionFileTemplateIsUnchanged(t *testing.T) { assert.NotContains(t, out, "turbolift create-prs completed") assert.NotContains(t, out, "2 OK, 0 skipped") - fakePrompt.AssertCalledWith(t, "It looks like the PR title and/or description has not been updated in README.md. Are you sure you want to proceed?") + fakePrompt.AssertCalledWith(t, "It looks like the PR title and/or description may not have been updated in README.md. Are you sure you want to proceed?") } func TestItWarnsIfDescriptionFileIsEmpty(t *testing.T) { @@ -66,7 +66,7 @@ func TestItWarnsIfDescriptionFileIsEmpty(t *testing.T) { assert.NotContains(t, out, "turbolift create-prs completed") assert.NotContains(t, out, "2 OK, 0 skipped") - fakePrompt.AssertCalledWith(t, "It looks like the PR title and/or description has not been updated in custom.md. Are you sure you want to proceed?") + fakePrompt.AssertCalledWith(t, "It looks like the PR title and/or description may not have been updated in custom.md. Are you sure you want to proceed?") } func TestItLogsCreatePrErrorsButContinuesToTryAll(t *testing.T) { diff --git a/internal/testsupport/testsupport.go b/internal/testsupport/testsupport.go index b7f2c58..63105fd 100644 --- a/internal/testsupport/testsupport.go +++ b/internal/testsupport/testsupport.go @@ -83,16 +83,7 @@ func CreateOrUpdatePrDescriptionFile(filename string, prTitle string, prBody str } func UseDefaultPrDescription(dirName string) { - originalPrTitle := fmt.Sprintf("TODO: Title of Pull Request (%s)", dirName) - originalPrBody := `TODO: This file will serve as both a README and the description of the PR. Describe the pull request using markdown in this file. Make it clear why the change is being made, and make suggestions for anything that the reviewer may need to do. - -By approving this PR, you are confirming that you have adequately and effectively reviewed this change. - -## How this change was made -TODO: Describe the approach that was used to select repositories for this change -TODO: Describe any shell commands, scripts, manual operations, etc, that were used to make changes - - -This PR was generated using [turbolift](https://github.com/Skyscanner/turbolift).` - CreateOrUpdatePrDescriptionFile("README.md", originalPrTitle, originalPrBody) + originalPrTitleTodo := fmt.Sprintf("TODO: Title of Pull Request (%s)", dirName) + originalPrBodyTodo := "TODO: This file will serve as both a README and the description of the PR." + CreateOrUpdatePrDescriptionFile("README.md", originalPrTitleTodo, originalPrBodyTodo) } From 877b5f0b2fc343d3456f5159e0eef7fe0a7003e2 Mon Sep 17 00:00:00 2001 From: Danny Ranson Date: Wed, 14 Aug 2024 11:37:57 +0100 Subject: [PATCH 3/5] test title and body separately --- cmd/create_prs/create_prs_test.go | 47 +++++++++++++++++++++++++++-- internal/testsupport/testsupport.go | 8 +++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/cmd/create_prs/create_prs_test.go b/cmd/create_prs/create_prs_test.go index 24bd371..7dab17e 100644 --- a/cmd/create_prs/create_prs_test.go +++ b/cmd/create_prs/create_prs_test.go @@ -22,6 +22,7 @@ import ( "github.com/skyscanner/turbolift/internal/prompt" "github.com/skyscanner/turbolift/internal/testsupport" "github.com/stretchr/testify/assert" + "path/filepath" "testing" ) @@ -33,8 +34,50 @@ func TestItWarnsIfDescriptionFileTemplateIsUnchanged(t *testing.T) { fakePrompt := prompt.NewFakePromptNo() p = fakePrompt - dir := testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2") - testsupport.UseDefaultPrDescription(dir) + dirName := testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2") + testsupport.UseDefaultPrDescription(dirName) + + out, err := runCommand() + assert.NoError(t, err) + assert.NotContains(t, out, "Creating PR in org/repo1") + assert.NotContains(t, out, "Creating PR in org/repo2") + assert.NotContains(t, out, "turbolift create-prs completed") + assert.NotContains(t, out, "2 OK, 0 skipped") + + fakePrompt.AssertCalledWith(t, "It looks like the PR title and/or description may not have been updated in README.md. Are you sure you want to proceed?") +} + +func TestItWarnsIfOnlyPrTitleIsUnchanged(t *testing.T) { + fakeGitHub := github.NewAlwaysFailsFakeGitHub() + gh = fakeGitHub + fakeGit := git.NewAlwaysSucceedsFakeGit() + g = fakeGit + fakePrompt := prompt.NewFakePromptNo() + p = fakePrompt + + dirName := testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2") + testsupport.UsePrTitleTodoOnly(filepath.Base(dirName)) + + out, err := runCommand() + assert.NoError(t, err) + assert.NotContains(t, out, "Creating PR in org/repo1") + assert.NotContains(t, out, "Creating PR in org/repo2") + assert.NotContains(t, out, "turbolift create-prs completed") + assert.NotContains(t, out, "2 OK, 0 skipped") + + fakePrompt.AssertCalledWith(t, "It looks like the PR title and/or description may not have been updated in README.md. Are you sure you want to proceed?") +} + +func TestItWarnsIfOnlyPrBodyIsUnchanged(t *testing.T) { + fakeGitHub := github.NewAlwaysFailsFakeGitHub() + gh = fakeGitHub + fakeGit := git.NewAlwaysSucceedsFakeGit() + g = fakeGit + fakePrompt := prompt.NewFakePromptNo() + p = fakePrompt + + testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2") + testsupport.UsePrBodyTodoOnly() out, err := runCommand() assert.NoError(t, err) diff --git a/internal/testsupport/testsupport.go b/internal/testsupport/testsupport.go index 63105fd..4c15d6d 100644 --- a/internal/testsupport/testsupport.go +++ b/internal/testsupport/testsupport.go @@ -87,3 +87,11 @@ func UseDefaultPrDescription(dirName string) { originalPrBodyTodo := "TODO: This file will serve as both a README and the description of the PR." CreateOrUpdatePrDescriptionFile("README.md", originalPrTitleTodo, originalPrBodyTodo) } + +func UsePrTitleTodoOnly(dirName string) { + CreateOrUpdatePrDescriptionFile("README.md", fmt.Sprintf("TODO: Title of Pull Request (%s)", dirName), "updated PR body") +} + +func UsePrBodyTodoOnly() { + CreateOrUpdatePrDescriptionFile("README.md", "updated PR title", "TODO: This file will serve as both a README and the description of the PR.") +} From 3187bcce5bff9221614c0d140a7f6312362e6188 Mon Sep 17 00:00:00 2001 From: Danny Ranson Date: Wed, 14 Aug 2024 11:51:41 +0100 Subject: [PATCH 4/5] no need to check dir name --- cmd/create_prs/create_prs.go | 4 ++-- cmd/create_prs/create_prs_test.go | 9 ++++----- internal/testsupport/testsupport.go | 8 ++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/cmd/create_prs/create_prs.go b/cmd/create_prs/create_prs.go index 1e3bc7d..2ad3983 100644 --- a/cmd/create_prs/create_prs.go +++ b/cmd/create_prs/create_prs.go @@ -142,7 +142,7 @@ func run(c *cobra.Command, _ []string) { } func prDescriptionUnchanged(dir *campaign.Campaign) bool { - originalPrTitleTodo := fmt.Sprintf("TODO: Title of Pull Request (%s)", dir.Name) + originalPrTitleTodo := "TODO: Title of Pull Request" originalPrBodyTodo := "TODO: This file will serve as both a README and the description of the PR." - return dir.PrTitle == originalPrTitleTodo || strings.Contains(dir.PrBody, originalPrBodyTodo) || dir.PrTitle == "" + return strings.Contains(dir.PrTitle, originalPrTitleTodo) || strings.Contains(dir.PrBody, originalPrBodyTodo) || dir.PrTitle == "" } diff --git a/cmd/create_prs/create_prs_test.go b/cmd/create_prs/create_prs_test.go index 7dab17e..672eb23 100644 --- a/cmd/create_prs/create_prs_test.go +++ b/cmd/create_prs/create_prs_test.go @@ -22,7 +22,6 @@ import ( "github.com/skyscanner/turbolift/internal/prompt" "github.com/skyscanner/turbolift/internal/testsupport" "github.com/stretchr/testify/assert" - "path/filepath" "testing" ) @@ -34,8 +33,8 @@ func TestItWarnsIfDescriptionFileTemplateIsUnchanged(t *testing.T) { fakePrompt := prompt.NewFakePromptNo() p = fakePrompt - dirName := testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2") - testsupport.UseDefaultPrDescription(dirName) + testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2") + testsupport.UseDefaultPrDescription() out, err := runCommand() assert.NoError(t, err) @@ -55,8 +54,8 @@ func TestItWarnsIfOnlyPrTitleIsUnchanged(t *testing.T) { fakePrompt := prompt.NewFakePromptNo() p = fakePrompt - dirName := testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2") - testsupport.UsePrTitleTodoOnly(filepath.Base(dirName)) + testsupport.PrepareTempCampaign(true, "org/repo1", "org/repo2") + testsupport.UsePrTitleTodoOnly() out, err := runCommand() assert.NoError(t, err) diff --git a/internal/testsupport/testsupport.go b/internal/testsupport/testsupport.go index 4c15d6d..0055ea2 100644 --- a/internal/testsupport/testsupport.go +++ b/internal/testsupport/testsupport.go @@ -82,14 +82,14 @@ func CreateOrUpdatePrDescriptionFile(filename string, prTitle string, prBody str } } -func UseDefaultPrDescription(dirName string) { - originalPrTitleTodo := fmt.Sprintf("TODO: Title of Pull Request (%s)", dirName) +func UseDefaultPrDescription() { + originalPrTitleTodo := "TODO: Title of Pull Request" originalPrBodyTodo := "TODO: This file will serve as both a README and the description of the PR." CreateOrUpdatePrDescriptionFile("README.md", originalPrTitleTodo, originalPrBodyTodo) } -func UsePrTitleTodoOnly(dirName string) { - CreateOrUpdatePrDescriptionFile("README.md", fmt.Sprintf("TODO: Title of Pull Request (%s)", dirName), "updated PR body") +func UsePrTitleTodoOnly() { + CreateOrUpdatePrDescriptionFile("README.md", "TODO: Title of Pull Request", "updated PR body") } func UsePrBodyTodoOnly() { From cb01fb5611c06d6a1aee7d08753411beccd8693a Mon Sep 17 00:00:00 2001 From: Danny Ranson Date: Wed, 14 Aug 2024 11:59:01 +0100 Subject: [PATCH 5/5] extract into vars --- internal/testsupport/testsupport.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/testsupport/testsupport.go b/internal/testsupport/testsupport.go index 0055ea2..e2d9a85 100644 --- a/internal/testsupport/testsupport.go +++ b/internal/testsupport/testsupport.go @@ -24,6 +24,9 @@ import ( "strings" ) +var originalPrTitleTodo = "TODO: Title of Pull Request" +var originalPrBodyTodo = "TODO: This file will serve as both a README and the description of the PR." + func Pwd() string { dir, _ := os.Getwd() return filepath.Base(dir) @@ -83,15 +86,13 @@ func CreateOrUpdatePrDescriptionFile(filename string, prTitle string, prBody str } func UseDefaultPrDescription() { - originalPrTitleTodo := "TODO: Title of Pull Request" - originalPrBodyTodo := "TODO: This file will serve as both a README and the description of the PR." CreateOrUpdatePrDescriptionFile("README.md", originalPrTitleTodo, originalPrBodyTodo) } func UsePrTitleTodoOnly() { - CreateOrUpdatePrDescriptionFile("README.md", "TODO: Title of Pull Request", "updated PR body") + CreateOrUpdatePrDescriptionFile("README.md", originalPrTitleTodo, "updated PR body") } func UsePrBodyTodoOnly() { - CreateOrUpdatePrDescriptionFile("README.md", "updated PR title", "TODO: This file will serve as both a README and the description of the PR.") + CreateOrUpdatePrDescriptionFile("README.md", "updated PR title", originalPrBodyTodo) }