-
Notifications
You must be signed in to change notification settings - Fork 519
Implement clone, commit and push of release notes draft to user's fork #1102
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,9 @@ import ( | |
"context" | ||
"encoding/json" | ||
"fmt" | ||
"io/ioutil" | ||
"os" | ||
"path/filepath" | ||
"strings" | ||
|
||
"github.com/blang/semver" | ||
|
@@ -34,6 +37,11 @@ import ( | |
"k8s.io/release/pkg/util" | ||
) | ||
|
||
const ( | ||
// draftFilename filename for the release notes draft | ||
draftFilename = "release-notes-draft.md" | ||
) | ||
|
||
// releaseNotesCmd represents the subcommand for `krel release-notes` | ||
var releaseNotesCmd = &cobra.Command{ | ||
Use: "release-notes", | ||
|
@@ -66,7 +74,13 @@ permissions to your fork of k/sig-release and k-sigs/release-notes.`, | |
} | ||
|
||
type releaseNotesOptions struct { | ||
tag string | ||
tag string | ||
draftOrg string | ||
draftRepo string | ||
createDraftPR bool | ||
outputDir string | ||
sigreleaseForkPath string | ||
Format string | ||
} | ||
|
||
type releaseNotesResult struct { | ||
|
@@ -85,6 +99,49 @@ func init() { | |
"version tag for the notes", | ||
) | ||
|
||
releaseNotesCmd.PersistentFlags().StringVar( | ||
&releaseNotesOpts.draftOrg, | ||
"draft-org", | ||
"", | ||
"a Github organization ownwer of the fork of k/sig-release where the Release Notes Draft PR will be created", | ||
) | ||
|
||
releaseNotesCmd.PersistentFlags().StringVar( | ||
&releaseNotesOpts.draftRepo, | ||
"draft-repo", | ||
"", | ||
saschagrunert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
"the name of the fork of k/sig-release, the Release Notes Draft PR will be created from this repository", | ||
) | ||
|
||
releaseNotesCmd.PersistentFlags().BoolVar( | ||
&releaseNotesOpts.createDraftPR, | ||
"create-draft-pr", | ||
false, | ||
"create the Release Notes Draft PR. --draft-org and --draft-repo must be set along with this option", | ||
) | ||
|
||
releaseNotesCmd.PersistentFlags().StringVarP( | ||
&releaseNotesOpts.outputDir, | ||
"output-dir", | ||
"o", | ||
".", | ||
"output a copy of the release notes to this directory", | ||
) | ||
|
||
releaseNotesCmd.PersistentFlags().StringVar( | ||
&releaseNotesOpts.Format, | ||
"format", | ||
util.EnvDefault("FORMAT", "markdown"), | ||
"The format for notes output (options: markdown, json)", | ||
) | ||
|
||
releaseNotesCmd.PersistentFlags().StringVar( | ||
&releaseNotesOpts.sigreleaseForkPath, | ||
"sigrelease-fork-path", | ||
filepath.Join(os.TempDir(), "k8s-sigrelease"), | ||
"output a copy of the release notes to this directory", | ||
) | ||
|
||
rootCmd.AddCommand(releaseNotesCmd) | ||
} | ||
|
||
|
@@ -113,12 +170,124 @@ func runReleaseNotes() (err error) { | |
logrus.Infof("Using start tag %v", start) | ||
logrus.Infof("Using end tag %v", tag) | ||
|
||
_, err = releaseNotesFrom(start) | ||
if releaseNotesOpts.createDraftPR { | ||
if err = validateDraftPROptions(); err != nil { | ||
return errors.Wrap(err, "validating PR command line options") | ||
} | ||
} | ||
|
||
result, err := releaseNotesFrom(start) | ||
if err != nil { | ||
return errors.Wrapf(err, "generating release notes") | ||
} | ||
|
||
//TODO: implement PR creation for k-sigs/release-notes and k/sig-release | ||
// Create RN draft PR | ||
if releaseNotesOpts.createDraftPR { | ||
if err := createDraftPR(tag, result); err != nil { | ||
return errors.Wrap(err, "failed to create release notes draft PR") | ||
} | ||
return nil | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think if do not specify an output option for the release notes we could print out the help screen. There are and will be more options in addition to --create-draft-pr in the subcommand. For example just writing the release notes to files or generating the json version. |
||
|
||
switch releaseNotesOpts.Format { | ||
case "json": | ||
err = ioutil.WriteFile(filepath.Join(releaseNotesOpts.outputDir, "release-notes.json"), []byte(result.json), 0644) | ||
if err != nil { | ||
return errors.Wrap(err, "writing release notes JSON file") | ||
} | ||
case "markdown": | ||
err = ioutil.WriteFile(filepath.Join(releaseNotesOpts.outputDir, "release-notes.md"), []byte(result.json), 0644) | ||
if err != nil { | ||
return errors.Wrap(err, "writing release notes markdown file") | ||
} | ||
default: | ||
return errors.Errorf("%q is an unsupported format", releaseNotesOpts.Format) | ||
} | ||
|
||
// TODO: implement PR creation for k-sigs/release-notes | ||
return nil | ||
} | ||
|
||
// validateDraftPROptions checks if we have all needed parameters to create the Release Notes PR | ||
func validateDraftPROptions() error { | ||
if releaseNotesOpts.createDraftPR { | ||
// Check if --draft-org is set | ||
if releaseNotesOpts.draftOrg == "" { | ||
logrus.Warn("cannot generate the Release Notes draft PR without --draft-org") | ||
} | ||
|
||
// Check if --draft-repo is set | ||
if releaseNotesOpts.draftRepo == "" { | ||
logrus.Warn("cannot generate the Release Notes draft PR without --draft-repo") | ||
} | ||
|
||
if releaseNotesOpts.draftOrg == "" || releaseNotesOpts.draftRepo == "" { | ||
return errors.New("To generate the release notes draft you must define both --draft-org and --draft-repo") | ||
} | ||
puerco marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
return nil | ||
} | ||
|
||
// createDraftPR pushes the release notes draft to the users fork | ||
func createDraftPR(tag string, result *releaseNotesResult) error { | ||
s, err := util.TagStringToSemver(tag) | ||
if err != nil { | ||
return errors.Wrapf(err, "no valid tag: %v", tag) | ||
} | ||
|
||
branchname := "release-notes-draft-" + tag | ||
|
||
// checkout kubernetes/sig-release | ||
sigReleaseRepo, err := git.CloneOrOpenGitHubRepo(releaseNotesOpts.sigreleaseForkPath, git.DefaultGithubOrg, git.DefaultGithubReleaseRepo, true) | ||
if err != nil { | ||
return errors.Wrap(err, "cloning k/sig-release") | ||
} | ||
|
||
// add the user's fork as a remote | ||
err = sigReleaseRepo.AddRemote("userfork", releaseNotesOpts.draftOrg, releaseNotesOpts.draftRepo) | ||
saschagrunert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err != nil { | ||
return errors.Wrap(err, "adding users fork as remote repository") | ||
} | ||
|
||
// verify the branch doesn't already exist on the user's fork | ||
err = sigReleaseRepo.HasRemoteBranch(branchname) | ||
if err == nil { | ||
return errors.Errorf("remote repo already has a branch named %s", branchname) | ||
} | ||
|
||
// checkout the new branch | ||
err = sigReleaseRepo.Checkout("-b", branchname) | ||
if err != nil { | ||
return errors.Wrapf(err, "creating new branch %s", branchname) | ||
} | ||
|
||
// generate the notes | ||
targetdir := filepath.Join(sigReleaseRepo.Dir(), "releases", fmt.Sprintf("release-%d.%d", s.Major, s.Minor)) | ||
logrus.Debugf("Release notes markdown will be written to %s", targetdir) | ||
err = ioutil.WriteFile(filepath.Join(targetdir, draftFilename), []byte(result.markdown), 0644) | ||
if err != nil { | ||
return errors.Wrapf(err, "writing release notes draft") | ||
} | ||
|
||
// commit the results | ||
err = sigReleaseRepo.Add(filepath.Join("releases", fmt.Sprintf("release-%d.%d", s.Major, s.Minor), draftFilename)) | ||
if err != nil { | ||
return errors.Wrap(err, "adding release notes draft to staging area") | ||
} | ||
|
||
err = sigReleaseRepo.Commit("Release Notes draft for k/k " + tag) | ||
if err != nil { | ||
return errors.Wrapf(err, "Error creating commit in %s/%s", releaseNotesOpts.draftOrg, releaseNotesOpts.draftRepo) | ||
} | ||
|
||
// push to fork | ||
logrus.Infof("Pushing release notes draft to %s/%s", releaseNotesOpts.draftOrg, releaseNotesOpts.draftRepo) | ||
err = sigReleaseRepo.PushToRemote("userfork", branchname) | ||
if err != nil { | ||
return errors.Wrapf(err, "pushing changes to %s/%s", releaseNotesOpts.draftOrg, releaseNotesOpts.draftRepo) | ||
} | ||
|
||
// TODO: Call github API and create PR against k/sig-release | ||
return nil | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,12 +39,13 @@ import ( | |
) | ||
|
||
const ( | ||
DefaultGithubOrg = "kubernetes" | ||
DefaultGithubRepo = "kubernetes" | ||
DefaultGithubURLBase = "https://github.com" | ||
DefaultRemote = "origin" | ||
DefaultMasterRef = "HEAD" | ||
Master = "master" | ||
DefaultGithubOrg = "kubernetes" | ||
DefaultGithubRepo = "kubernetes" | ||
DefaultGithubReleaseRepo = "sig-release" | ||
DefaultGithubURLBase = "https://github.com" | ||
DefaultRemote = "origin" | ||
DefaultMasterRef = "HEAD" | ||
Master = "master" | ||
|
||
branchRE = `master|release-([0-9]{1,})\.([0-9]{1,})(\.([0-9]{1,}))*$` | ||
defaultGithubAuthRoot = "[email protected]:" | ||
|
@@ -747,3 +748,26 @@ func (r *Repo) Rm(force bool, files ...string) error { | |
NewWithWorkDir(r.Dir(), gitExecutable, args...). | ||
RunSilentSuccess() | ||
} | ||
|
||
// AddRemote adds a new remote to the current working tree | ||
func (r *Repo) AddRemote(name, owner, repo string) error { | ||
saschagrunert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
args := []string{"remote", "add"} | ||
args = append(args, name, fmt.Sprintf("%s%s/%s.git", defaultGithubAuthRoot, owner, repo)) | ||
|
||
return command. | ||
NewWithWorkDir(r.Dir(), gitExecutable, args...). | ||
RunSilentSuccess() | ||
} | ||
|
||
// PushToRemote push the current branch to a spcified remote, but only if the | ||
// repository is not in dry run mode | ||
func (r *Repo) PushToRemote(remote, remoteBranch string) error { | ||
args := []string{"push"} | ||
if r.dryRun { | ||
logrus.Infof("Won't push due to dry run repository") | ||
args = append(args, "--dry-run") | ||
} | ||
args = append(args, remote, remoteBranch) | ||
|
||
return command.NewWithWorkDir(r.Dir(), gitExecutable, args...).RunSuccess() | ||
} |
Uh oh!
There was an error while loading. Please reload this page.