Skip to content

Commit 0601453

Browse files
committed
Always use a clean checkout for clones during release-notes
This avoids a large category of failure modes where an existing on-disk repository is in an unexpected state. We also delete the repo afterwards to avoid multiple runs filling up the disk. This change therefore removes configuration flags for the fork locations. Also included: * Set the upstream when pushing a new branch * Use HTTP transport for git instead of SSH As we are now using the GitHub token for authentication, we need the HTTP URL and not the SSH one. * Remove check for branch existing on remote The existing code does not work for a number of reasons: - It only checks the default remote, not the user's fork. - Any error returned would be treated as a success, not just a "not found". Even without this check there is no risk, as GitHub will simply reject the pushing of a new branch without `--force`, which we do not use. * Use existing branch if it already exists The `-B` flag to `git checkout` will create the branch if it does not exist (existing beaviour) but will also use it if it does exist.
1 parent 01df93c commit 0601453

File tree

3 files changed

+39
-47
lines changed

3 files changed

+39
-47
lines changed

cmd/krel/cmd/release_notes.go

Lines changed: 26 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,15 @@ permissions to your fork of k/sig-release and k-sigs/release-notes.`,
8787
}
8888

8989
type releaseNotesOptions struct {
90-
tag string
91-
draftOrg string
92-
draftRepo string
93-
createDraftPR bool
94-
createWebsitePR bool
95-
outputDir string
96-
sigreleaseForkPath string
97-
kubernetessigsForkPath string
98-
Format string
99-
websiteOrg string
100-
websiteRepo string
90+
tag string
91+
draftOrg string
92+
draftRepo string
93+
createDraftPR bool
94+
createWebsitePR bool
95+
outputDir string
96+
Format string
97+
websiteOrg string
98+
websiteRepo string
10199
}
102100

103101
type releaseNotesResult struct {
@@ -173,20 +171,6 @@ func init() {
173171
"The format for notes output (options: markdown, json)",
174172
)
175173

176-
releaseNotesCmd.PersistentFlags().StringVar(
177-
&releaseNotesOpts.kubernetessigsForkPath,
178-
"kubernetes-sigs-fork-path",
179-
filepath.Join(os.TempDir(), "k8s-sigs"),
180-
"fork kubernetes-sigs/release-notes and output a copy of the json release notes to this directory",
181-
)
182-
183-
releaseNotesCmd.PersistentFlags().StringVar(
184-
&releaseNotesOpts.sigreleaseForkPath,
185-
"sigrelease-fork-path",
186-
filepath.Join(os.TempDir(), "k8s-sigrelease"),
187-
"fork k/sig-release and output a copy of the release notes draft to this directory",
188-
)
189-
190174
rootCmd.AddCommand(releaseNotesCmd)
191175
}
192176

@@ -328,7 +312,7 @@ func validateWebsitePROptions() error {
328312
}
329313

330314
// createDraftPR pushes the release notes draft to the users fork
331-
func createDraftPR(tag string, result *releaseNotesResult) error {
315+
func createDraftPR(tag string, result *releaseNotesResult) (err error) {
332316
s, err := util.TagStringToSemver(tag)
333317
if err != nil {
334318
return errors.Wrapf(err, "no valid tag: %v", tag)
@@ -338,14 +322,18 @@ func createDraftPR(tag string, result *releaseNotesResult) error {
338322

339323
// Prepare the fork of k/sig-release
340324
sigReleaseRepo, err := prepareFork(
341-
branchname, releaseNotesOpts.sigreleaseForkPath,
325+
branchname,
342326
git.DefaultGithubOrg, git.DefaultGithubReleaseRepo,
343327
releaseNotesOpts.draftOrg, releaseNotesOpts.draftRepo,
344328
)
345329
if err != nil {
346330
return errors.Wrap(err, "preparing local fork of kubernetes/sig-release")
347331
}
348332

333+
defer func() {
334+
err = sigReleaseRepo.Cleanup()
335+
}()
336+
349337
// generate the notes
350338
targetdir := filepath.Join(sigReleaseRepo.Dir(), "releases", fmt.Sprintf("release-%d.%d", s.Major, s.Minor))
351339
logrus.Debugf("release notes markdown will be written to %v", targetdir)
@@ -375,19 +363,19 @@ func createDraftPR(tag string, result *releaseNotesResult) error {
375363
}
376364

377365
// prepareFork Prepare a branch a repo
378-
func prepareFork(branchName, repoPath, upstreamOrg, upstreamRepo, myOrg, myRepo string) (repo *git.Repo, err error) {
366+
func prepareFork(branchName, upstreamOrg, upstreamRepo, myOrg, myRepo string) (repo *git.Repo, err error) {
379367
// checkout the upstream repository
380368
logrus.Infof("cloning/updating repository %s/%s", upstreamOrg, upstreamRepo)
381369

382-
repo, err = git.CloneOrOpenGitHubRepo(
383-
repoPath, upstreamOrg, upstreamRepo, true,
370+
repo, err = git.CleanCloneGitHubRepo(
371+
upstreamOrg, upstreamRepo, false,
384372
)
385373
if err != nil {
386374
return nil, errors.Wrapf(err, "cloning %s/%s", upstreamOrg, upstreamRepo)
387375
}
388376

389377
// test if the fork remote is already existing
390-
url := git.GetRepoURL(myOrg, myRepo, true)
378+
url := git.GetRepoURL(myOrg, myRepo, false)
391379
if repo.HasRemote(userForkName, url) {
392380
logrus.Infof(
393381
"Using already existing remote %v (%v) in repository",
@@ -401,14 +389,8 @@ func prepareFork(branchName, repoPath, upstreamOrg, upstreamRepo, myOrg, myRepo
401389
}
402390
}
403391

404-
// verify the branch doesn't already exist on the user's fork
405-
err = repo.HasRemoteBranch(branchName)
406-
if err == nil {
407-
return nil, errors.Errorf("remote repo already has a branch named %s", branchName)
408-
}
409-
410392
// checkout the new branch
411-
err = repo.Checkout("-b", branchName)
393+
err = repo.Checkout("-B", branchName)
412394
if err != nil {
413395
return nil, errors.Wrapf(err, "creating new branch %s", branchName)
414396
}
@@ -483,8 +465,8 @@ func processJSONOutput(repoPath string) error {
483465
}
484466

485467
// createWebsitePR creates the JSON version of the release notes and pushes them to a user fork
486-
func createWebsitePR(tag string, result *releaseNotesResult) error {
487-
_, err := util.TagStringToSemver(tag)
468+
func createWebsitePR(tag string, result *releaseNotesResult) (err error) {
469+
_, err = util.TagStringToSemver(tag)
488470
if err != nil {
489471
return errors.Wrapf(err, "no valid tag: %v", tag)
490472
}
@@ -494,12 +476,15 @@ func createWebsitePR(tag string, result *releaseNotesResult) error {
494476

495477
// checkout kubernetes-sigs/release-notes
496478
k8sSigsRepo, err := prepareFork(
497-
branchname, releaseNotesOpts.kubernetessigsForkPath, defaultKubernetesSigsOrg,
479+
branchname, defaultKubernetesSigsOrg,
498480
defaultKubernetesSigsRepo, releaseNotesOpts.websiteOrg, releaseNotesOpts.websiteRepo,
499481
)
500482
if err != nil {
501483
return errors.Wrap(err, "preparing local fork branch")
502484
}
485+
defer func() {
486+
err = k8sSigsRepo.Cleanup()
487+
}()
503488

504489
// add a reference to the new json file in assets.ts
505490
if err := addReferenceToAssetsFile(k8sSigsRepo.Dir(), jsonNotesFilename); err != nil {

docs/krel/release-notes.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,7 @@ Before running `krel release-notes` export your GitHub token to \$GITHUB_TOKEN:
4040
--draft-repo string the name of the fork of k/sig-release, the Release Notes Draft PR will be created from this repository (default "sig-release")
4141
--format string The format for notes output (options: markdown, json) (default "markdown")
4242
-h, --help help for release-notes
43-
--kubernetes-sigs-fork-path string fork kubernetes-sigs/release-notes and output a copy of the json release notes to this directory (default "/tmp/k8s-sigs")
4443
-o, --output-dir string output a copy of the release notes to this directory (default ".")
45-
--sigrelease-fork-path string fork k/sig-release and output a copy of the release notes draft to this directory (default "/tmp/k8s-sigrelease")
4644
-t, --tag string version tag for the notes
4745
--website-org string a Github organization owner of the fork of kuberntets-sigs/release-notes where the Website PR will be created
4846
--website-repo string the name of the fork of kuberntets-sigs/release-notes, the Release Notes Draft PR will be created from this repository (default "release-notes")

pkg/git/git.go

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,9 +198,18 @@ func CloneOrOpenDefaultGitHubRepoSSH(repoPath string) (*Repo, error) {
198198
)
199199
}
200200

201-
// CloneOrOpenGitHubRepo creates a temp directory containing the provided
202-
// GitHub repository via the owner and repo. If useSSH is true, then it will
203-
// clone the repository using the defaultGithubAuthRoot.
201+
// CleanCloneGitHubRepo creates a guaranteed fresh checkout of a given repository. The returned *Repo has a Cleanup()
202+
// method that should be used to delete the repository on-disk afterwards.
203+
func CleanCloneGitHubRepo(owner, repo string, useSSH bool) (*Repo, error) {
204+
repoURL := GetRepoURL(owner, repo, useSSH)
205+
// The use of a blank string for the repo path triggers special behaviour in CloneOrOpenRepo that causes a true
206+
// temporary directory with a random name to be created.
207+
return CloneOrOpenRepo("", repoURL, useSSH)
208+
}
209+
210+
// CloneOrOpenGitHubRepo works with a repository in the given directory, or creates one if the directory is empty. The
211+
// repo uses the provided GitHub repository via the owner and repo. If useSSH is true, then it will clone the
212+
// repository using the defaultGithubAuthRoot.
204213
func CloneOrOpenGitHubRepo(repoPath, owner, repo string, useSSH bool) (*Repo, error) {
205214
repoURL := GetRepoURL(owner, repo, useSSH)
206215
return CloneOrOpenRepo(repoPath, repoURL, useSSH)
@@ -843,7 +852,7 @@ func (r *Repo) AddRemote(name, owner, repo string) error {
843852
// PushToRemote push the current branch to a spcified remote, but only if the
844853
// repository is not in dry run mode
845854
func (r *Repo) PushToRemote(remote, remoteBranch string) error {
846-
args := []string{"push"}
855+
args := []string{"push", "--set-upstream"}
847856
if r.dryRun {
848857
logrus.Infof("Won't push due to dry run repository")
849858
args = append(args, "--dry-run")

0 commit comments

Comments
 (0)