mirror of
https://github.com/hashicorp/vault.git
synced 2025-08-23 15:41:07 +02:00
Add a new `github create backport` sub-command that can create a backport of a given pull request. The command has been designed around a Github Actions workflow where it is triggered on a closed pull request event with a guard that checks for merges: ```yaml pull_request_target: types: closed jobs: backport: if: github.even.pull_request.merged runs-on: "..." ``` Eventually this sub-command (or another similar one) can be used to implemente backporting a CE pull request to the corresponding ce/* branch in vault-enterprise. This functionality will be implemented in VAULT-34827. This backport runner has several new behaviors not present in the existing backport assistant: - If the source PR was made against an enterprise branch we'll assume that we want create a CE backport. - Enterprise only files will be automatically _removed_ from the CE backport for you. This will not guarantee a working CE pull request but does quite a bit of the heavy lifting for you. - If the change only contains enterprise files we'll skip creating a CE backport. - If the corresponding CE branch is inactive (as defined in .release/versions.hcl) then we will skip creating a backport in most cases. The exceptions are changes that include docs, README, or pipeline changes as we assume that even active branches will want those changes. - Backport labels still work but _only_ to enterprise PR's. It is assumed that when the subsequent PRs are merged that their corresponding CE backports will be created. - Backport labels no longer include editions. They will now use the same schema as active versions defined .release/verions.hcl. E.g. `backport/1.19.x`. `main` is always assumed to be active. - The runner will always try and update the source PR with a Github comment regarding the status of each individual backport. Even if one attempt at backporting fails we'll continue until we've attempted all backports. Signed-off-by: Ryan Cragun <me@ryan.ec>
117 lines
4.9 KiB
Go
117 lines
4.9 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
|
|
"github.com/hashicorp/vault/tools/pipeline/internal/pkg/changed"
|
|
"github.com/hashicorp/vault/tools/pipeline/internal/pkg/github"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
var createGithubBackportState struct {
|
|
req github.CreateBackportReq
|
|
ceExclude []string
|
|
ceAllowInactive []string
|
|
}
|
|
|
|
func newGithubCreateBackportCmd() *cobra.Command {
|
|
listRuns := &cobra.Command{
|
|
Use: "backport 1234",
|
|
Short: "Create a backport pull request from another pull request",
|
|
Long: "Create a backport pull request from another pull request",
|
|
RunE: runCreateGithubBackportCmd,
|
|
Args: func(cmd *cobra.Command, args []string) error {
|
|
switch len(args) {
|
|
case 1:
|
|
pr, err := strconv.ParseUint(args[0], 10, 0)
|
|
if err != nil {
|
|
return fmt.Errorf("invalid pull number: %s: %w", args[0], err)
|
|
}
|
|
if pr <= math.MaxUint32 {
|
|
createGithubBackportState.req.PullNumber = uint(pr)
|
|
} else {
|
|
return fmt.Errorf("invalid pull number: %s: number is too large", args[0])
|
|
}
|
|
return nil
|
|
case 0:
|
|
return errors.New("no pull request number has been provided")
|
|
default:
|
|
return fmt.Errorf("invalid arguments: only pull request number is expected, received %d arguments: %v", len(args), args)
|
|
}
|
|
},
|
|
}
|
|
|
|
listRuns.PersistentFlags().StringSliceVarP(&createGithubBackportState.ceAllowInactive, "ce-allow-inactive-groups", "a", []string{"docs", "changelog", "pipeline"}, "Change file groups that should be allowed to backport to inactive CE branches")
|
|
listRuns.PersistentFlags().StringVar(&createGithubBackportState.req.CEBranchPrefix, "ce-branch-prefix", "ce", "The branch name prefix")
|
|
listRuns.PersistentFlags().StringSliceVarP(&createGithubBackportState.ceExclude, "ce-exclude-groups", "e", []string{"enterprise"}, "Change file groups that should be excluded from the backporting to CE branches")
|
|
listRuns.PersistentFlags().StringVar(&createGithubBackportState.req.BaseOrigin, "base-origin", "origin", "The name to use for the base remote origin")
|
|
listRuns.PersistentFlags().StringVarP(&createGithubBackportState.req.Owner, "owner", "o", "hashicorp", "The Github organization")
|
|
listRuns.PersistentFlags().StringVarP(&createGithubBackportState.req.Repo, "repo", "r", "vault-enterprise", "The Github repository. Private repositories require auth via a GITHUB_TOKEN env var")
|
|
listRuns.PersistentFlags().StringVarP(&createGithubBackportState.req.RepoDir, "repo-dir", "d", "", "The path to the vault repository dir. If not set a temporary directory will be used")
|
|
listRuns.PersistentFlags().StringVarP(&createGithubBackportState.req.ReleaseVersionConfigPath, "releases-version-path", "m", "", "The path to .release/versions.hcl")
|
|
listRuns.PersistentFlags().UintVar(&createGithubBackportState.req.ReleaseRecurseDepth, "recurse", 3, "If no path to a config file is given, recursively search backwards for it and stop at root or until we've his the configured depth.")
|
|
|
|
// NOTE: The following are technically flags but they only for testing testing
|
|
// the command before we cut over to new utility.
|
|
listRuns.PersistentFlags().StringVar(&createGithubBackportState.req.EntBranchPrefix, "ent-branch-prefix", "", "The ent branch name prefix. Only used for testing before migration to the new workflow")
|
|
listRuns.PersistentFlags().StringVar(&createGithubBackportState.req.BackportLabelPrefix, "backport-label-prefix", "backport", "The name to use for the base remote origin")
|
|
|
|
err := listRuns.PersistentFlags().MarkHidden("ent-branch-prefix")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
err = listRuns.PersistentFlags().MarkHidden("backport-label-prefix")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return listRuns
|
|
}
|
|
|
|
func runCreateGithubBackportCmd(cmd *cobra.Command, args []string) error {
|
|
cmd.SilenceUsage = true // Don't spam the usage on failure
|
|
|
|
for i, ig := range createGithubBackportState.ceAllowInactive {
|
|
if i == 0 && createGithubBackportState.req.CEAllowInactiveGroups == nil {
|
|
createGithubBackportState.req.CEAllowInactiveGroups = changed.FileGroups{}
|
|
}
|
|
createGithubBackportState.req.CEAllowInactiveGroups = createGithubBackportState.req.CEAllowInactiveGroups.Add(changed.FileGroup(ig))
|
|
}
|
|
|
|
for i, eg := range createGithubBackportState.ceExclude {
|
|
if i == 0 && createGithubBackportState.req.CEExclude == nil {
|
|
createGithubBackportState.req.CEExclude = changed.FileGroups{}
|
|
}
|
|
createGithubBackportState.req.CEExclude = createGithubBackportState.req.CEExclude.Add(changed.FileGroup(eg))
|
|
}
|
|
|
|
res := createGithubBackportState.req.Run(context.TODO(), githubCmdState.Github, githubCmdState.Git)
|
|
if res == nil {
|
|
res = &github.CreateBackportRes{}
|
|
}
|
|
if err := res.Err(); err != nil {
|
|
res.ErrorMessage = err.Error()
|
|
}
|
|
|
|
switch rootCfg.format {
|
|
case "json":
|
|
b, err := res.ToJSON()
|
|
if err != nil {
|
|
return errors.Join(res.Err(), err)
|
|
}
|
|
fmt.Println(string(b))
|
|
default:
|
|
fmt.Println(res.ToTable().Render())
|
|
}
|
|
|
|
return res.Err()
|
|
}
|